@ai-sdk/langchain 3.0.30 → 3.0.32

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,24 @@
1
1
  # @ai-sdk/langchain
2
2
 
3
+ ## 3.0.32
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [6cd7c74]
8
+ - Updated dependencies [e35bcae]
9
+ - Updated dependencies [a4eb3f3]
10
+ - ai@7.0.32
11
+
12
+ ## 3.0.31
13
+
14
+ ### Patch Changes
15
+
16
+ - 223b5c3: feat(langchain): allow suppressing outer start and finish UI message chunks
17
+ - e864c10: fix(langchain): close LangGraph reasoning before starting text
18
+ - Updated dependencies [70f18c3]
19
+ - Updated dependencies [cd06458]
20
+ - ai@7.0.31
21
+
3
22
  ## 3.0.30
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -221,14 +221,16 @@ Converts AI SDK `ModelMessage` objects to LangChain `BaseMessage` objects.
221
221
 
222
222
  **Returns:** `BaseMessage[]`
223
223
 
224
- ### `toUIMessageStream(stream, callbacks?)`
224
+ ### `toUIMessageStream(stream, options?)`
225
225
 
226
226
  Converts a LangChain/LangGraph stream to an AI SDK `UIMessageStream`.
227
227
 
228
228
  **Parameters:**
229
229
 
230
230
  - `stream`: `AsyncIterable | ReadableStream` - A stream from LangChain `model.stream()`, LangGraph `graph.stream()`, or `streamEvents()`
231
- - `callbacks?`: `StreamCallbacks<TState>` - Optional lifecycle callbacks:
231
+ - `options?`: `ToUIMessageStreamOptions<TState>` - Optional lifecycle controls and callbacks:
232
+ - `sendStart` - Whether to emit the outer `start` chunk (defaults to `true`)
233
+ - `sendFinish` - Whether to emit the outer `finish` chunk (defaults to `true`)
232
234
  - `onStart()` - Called when stream initializes
233
235
  - `onToken(token)` - Called for each token
234
236
  - `onText(text)` - Called for each text chunk
@@ -239,6 +241,33 @@ Converts a LangChain/LangGraph stream to an AI SDK `UIMessageStream`.
239
241
 
240
242
  **Returns:** `ReadableStream<UIMessageChunk>`
241
243
 
244
+ When composing the adapter output into a stream that owns the message
245
+ lifecycle, set `sendStart` and `sendFinish` to `false`:
246
+
247
+ ```ts
248
+ const stream = createUIMessageStream({
249
+ async execute({ writer }) {
250
+ writer.write({ type: 'start' });
251
+
252
+ const reader = toUIMessageStream(langchainStream, {
253
+ sendStart: false,
254
+ sendFinish: false,
255
+ }).getReader();
256
+
257
+ while (true) {
258
+ const { done, value: chunk } = await reader.read();
259
+ if (done) break;
260
+ writer.write(chunk);
261
+ }
262
+
263
+ writer.write({ type: 'finish' });
264
+ },
265
+ });
266
+ ```
267
+
268
+ Only the outer lifecycle chunks are omitted. Text, reasoning, tool, data, and
269
+ step chunks are still emitted.
270
+
242
271
  **Supported stream types:**
243
272
 
244
273
  - **Model streams** - Direct `AIMessageChunk` streams from `model.stream()`
package/dist/index.d.ts CHANGED
@@ -25,6 +25,23 @@ interface StreamCallbacks<TState = unknown> {
25
25
  onAbort?: () => Promise<void> | void;
26
26
  }
27
27
 
28
+ /**
29
+ * Options for converting a LangChain stream to an AI SDK UIMessageStream.
30
+ */
31
+ interface ToUIMessageStreamOptions<TState = unknown> extends StreamCallbacks<TState> {
32
+ /**
33
+ * Whether to emit the outer `start` chunk.
34
+ *
35
+ * @default true
36
+ */
37
+ sendStart?: boolean;
38
+ /**
39
+ * Whether to emit the outer `finish` chunk.
40
+ *
41
+ * @default true
42
+ */
43
+ sendFinish?: boolean;
44
+ }
28
45
  /**
29
46
  * Converts AI SDK UIMessages to LangChain BaseMessage objects.
30
47
  *
@@ -61,7 +78,7 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
61
78
  * - streamEvents streams (from `agent.streamEvents()` or `model.streamEvents()`)
62
79
  *
63
80
  * @param stream - A stream from LangChain model.stream(), graph.stream(), or streamEvents().
64
- * @param callbacks - Optional callbacks for stream lifecycle events.
81
+ * @param options - Optional lifecycle callbacks and outer chunk controls.
65
82
  * @returns A ReadableStream of UIMessageChunk objects.
66
83
  *
67
84
  * @example
@@ -109,7 +126,7 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
109
126
  * });
110
127
  * ```
111
128
  */
112
- declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks<TState>): ReadableStream<UIMessageChunk>;
129
+ declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, options?: ToUIMessageStreamOptions<TState>): ReadableStream<UIMessageChunk>;
113
130
 
114
131
  /**
115
132
  * Options for configuring a LangSmith deployment transport.
@@ -156,4 +173,4 @@ declare class LangSmithDeploymentTransport<UI_MESSAGE extends UIMessage> impleme
156
173
  } & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
157
174
  }
158
175
 
159
- export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StreamCallbacks, convertModelMessages, toBaseMessages, toUIMessageStream };
176
+ export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StreamCallbacks, type ToUIMessageStreamOptions, convertModelMessages, toBaseMessages, toUIMessageStream };
package/dist/index.js CHANGED
@@ -730,6 +730,10 @@ function processLangGraphEvent(event, state, controller) {
730
730
  const text = getMessageText(msg);
731
731
  if (text) {
732
732
  const seen = messageSeen.get(msgId);
733
+ if ((seen == null ? void 0 : seen.reasoning) && !seen.text) {
734
+ controller.enqueue({ type: "reasoning-end", id: msgId });
735
+ seen.reasoning = false;
736
+ }
733
737
  if (!(seen == null ? void 0 : seen.text)) {
734
738
  controller.enqueue({ type: "text-start", id: msgId });
735
739
  getOrCreateMessageSeen(messageSeen, msgId).text = true;
@@ -1190,7 +1194,8 @@ function isAbortError(error) {
1190
1194
  }
1191
1195
  return false;
1192
1196
  }
1193
- function toUIMessageStream(stream, callbacks) {
1197
+ function toUIMessageStream(stream, options) {
1198
+ const { sendStart = true, sendFinish = true } = options != null ? options : {};
1194
1199
  const textChunks = [];
1195
1200
  let lastValuesData;
1196
1201
  const modelState = {
@@ -1240,10 +1245,12 @@ function toUIMessageStream(stream, callbacks) {
1240
1245
  error: (e) => originalController.error(e),
1241
1246
  enqueue: (chunk) => {
1242
1247
  var _a, _b;
1243
- if (callbacks && chunk.type === "text-delta" && chunk.delta) {
1244
- textChunks.push(chunk.delta);
1245
- (_a = callbacks.onToken) == null ? void 0 : _a.call(callbacks, chunk.delta);
1246
- (_b = callbacks.onText) == null ? void 0 : _b.call(callbacks, chunk.delta);
1248
+ if (chunk.type === "text-delta" && chunk.delta) {
1249
+ if (options == null ? void 0 : options.onFinal) {
1250
+ textChunks.push(chunk.delta);
1251
+ }
1252
+ (_a = options == null ? void 0 : options.onToken) == null ? void 0 : _a.call(options, chunk.delta);
1253
+ (_b = options == null ? void 0 : options.onText) == null ? void 0 : _b.call(options, chunk.delta);
1247
1254
  }
1248
1255
  originalController.enqueue(chunk);
1249
1256
  }
@@ -1252,9 +1259,11 @@ function toUIMessageStream(stream, callbacks) {
1252
1259
  return new ReadableStream({
1253
1260
  async start(controller) {
1254
1261
  var _a, _b, _c, _d, _e, _f, _g, _h;
1255
- await ((_a = callbacks == null ? void 0 : callbacks.onStart) == null ? void 0 : _a.call(callbacks));
1262
+ await ((_a = options == null ? void 0 : options.onStart) == null ? void 0 : _a.call(options));
1256
1263
  const wrappedController = createCallbackController(controller);
1257
- controller.enqueue({ type: "start" });
1264
+ if (sendStart) {
1265
+ controller.enqueue({ type: "start" });
1266
+ }
1258
1267
  try {
1259
1268
  while (true) {
1260
1269
  const { done, value } = await iterator.next();
@@ -1306,7 +1315,9 @@ function toUIMessageStream(stream, callbacks) {
1306
1315
  id: (_c = modelState.textMessageId) != null ? _c : modelState.messageId
1307
1316
  });
1308
1317
  }
1309
- controller.enqueue({ type: "finish" });
1318
+ if (sendFinish) {
1319
+ controller.enqueue({ type: "finish" });
1320
+ }
1310
1321
  } else if (streamType === "langgraph") {
1311
1322
  for (const [id, seen] of langGraphState.messageSeen) {
1312
1323
  if (seen.text) {
@@ -1319,17 +1330,19 @@ function toUIMessageStream(stream, callbacks) {
1319
1330
  if (langGraphState.currentStep !== null) {
1320
1331
  controller.enqueue({ type: "finish-step" });
1321
1332
  }
1322
- controller.enqueue({ type: "finish" });
1333
+ if (sendFinish) {
1334
+ controller.enqueue({ type: "finish" });
1335
+ }
1323
1336
  }
1324
- await ((_d = callbacks == null ? void 0 : callbacks.onFinal) == null ? void 0 : _d.call(callbacks, textChunks.join("")));
1325
- await ((_e = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _e.call(callbacks, lastValuesData));
1337
+ await ((_d = options == null ? void 0 : options.onFinal) == null ? void 0 : _d.call(options, textChunks.join("")));
1338
+ await ((_e = options == null ? void 0 : options.onFinish) == null ? void 0 : _e.call(options, lastValuesData));
1326
1339
  } catch (error) {
1327
1340
  const errorObj = error instanceof Error ? error : new Error(String(error));
1328
- await ((_f = callbacks == null ? void 0 : callbacks.onFinal) == null ? void 0 : _f.call(callbacks, textChunks.join("")));
1341
+ await ((_f = options == null ? void 0 : options.onFinal) == null ? void 0 : _f.call(options, textChunks.join("")));
1329
1342
  if (isAbortError(error)) {
1330
- await ((_g = callbacks == null ? void 0 : callbacks.onAbort) == null ? void 0 : _g.call(callbacks));
1343
+ await ((_g = options == null ? void 0 : options.onAbort) == null ? void 0 : _g.call(options));
1331
1344
  } else {
1332
- await ((_h = callbacks == null ? void 0 : callbacks.onError) == null ? void 0 : _h.call(callbacks, errorObj));
1345
+ await ((_h = options == null ? void 0 : options.onError) == null ? void 0 : _h.call(options, errorObj));
1333
1346
  }
1334
1347
  controller.enqueue({
1335
1348
  type: "error",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts","../src/utils.ts","../src/transport.ts"],"sourcesContent":["import {\n SystemMessage,\n type BaseMessage,\n type AIMessageChunk,\n} from '@langchain/core/messages';\nimport {\n convertToModelMessages,\n type UIMessage,\n type UIMessageChunk,\n type ModelMessage,\n} from 'ai';\nimport {\n convertToolResultPart,\n convertAssistantContent,\n convertUserContent,\n processModelChunk,\n processLangGraphEvent,\n parseLangGraphEvent,\n isToolResultPart,\n extractReasoningFromContentBlocks,\n extractCitationsFromContentBlocks,\n emitSourceChunks,\n} from './utils';\nimport type { LangGraphEventState } from './types';\nimport type { StreamCallbacks } from './stream-callbacks';\n\n/**\n * Converts AI SDK UIMessages to LangChain BaseMessage objects.\n *\n * This function transforms the AI SDK's message format into LangChain's message\n * format, enabling seamless integration between the two frameworks.\n *\n * @param messages - Array of AI SDK UIMessage objects to convert.\n * @returns Promise resolving to an array of LangChain BaseMessage objects.\n *\n * @example\n * ```ts\n * import { toBaseMessages } from '@ai-sdk/langchain';\n *\n * const langchainMessages = await toBaseMessages(uiMessages);\n *\n * // Use with LangChain\n * const response = await model.invoke(langchainMessages);\n * ```\n */\nexport async function toBaseMessages(\n messages: UIMessage[],\n): Promise<BaseMessage[]> {\n const modelMessages = await convertToModelMessages(messages);\n return convertModelMessages(modelMessages);\n}\n\n/**\n * Converts ModelMessages to LangChain BaseMessage objects.\n *\n * @param modelMessages - Array of ModelMessage objects from convertToModelMessages.\n * @returns Array of LangChain BaseMessage objects.\n */\nexport function convertModelMessages(\n modelMessages: ModelMessage[],\n): BaseMessage[] {\n const result: BaseMessage[] = [];\n\n for (const message of modelMessages) {\n switch (message.role) {\n case 'tool': {\n // Tool messages contain an array of tool results\n for (const item of message.content) {\n if (isToolResultPart(item)) {\n result.push(convertToolResultPart(item));\n }\n }\n break;\n }\n\n case 'assistant': {\n result.push(convertAssistantContent(message.content));\n break;\n }\n\n case 'system': {\n result.push(new SystemMessage({ content: message.content }));\n break;\n }\n\n case 'user': {\n result.push(convertUserContent(message.content));\n break;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Type guard to check if a value is a streamEvents event object.\n * streamEvents produces objects with `event` and `data` properties.\n *\n * @param value - The value to check.\n * @returns True if the value is a streamEvents event object.\n */\nfunction isStreamEventsEvent(\n value: unknown,\n): value is { event: string; data: Record<string, unknown> } {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n // Check for event property being a string\n if (!('event' in obj) || typeof obj.event !== 'string') return false;\n // Check for data property being an object (but allow null/undefined)\n if (!('data' in obj)) return false;\n // data can be null in some events, treat as empty object\n return obj.data === null || typeof obj.data === 'object';\n}\n\n/**\n * Processes a streamEvents event and emits UI message chunks.\n *\n * @param event - The streamEvents event to process.\n * @param state - The state for tracking stream progress.\n * @param controller - The controller to emit UI message chunks.\n */\nfunction processStreamEventsEvent(\n event: {\n event: string;\n data: Record<string, unknown> | null;\n run_id?: string;\n name?: string;\n },\n state: {\n started: boolean;\n messageId: string;\n reasoningStarted: boolean;\n textStarted: boolean;\n textMessageId: string | null;\n reasoningMessageId: string | null;\n emittedSourceIds: Set<string>;\n },\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n /**\n * Capture run_id from event level if available (streamEvents v2 format)\n */\n if (event.run_id && !state.started) {\n state.messageId = event.run_id;\n }\n\n /**\n * Skip events with null/undefined data\n */\n if (!event.data) return;\n\n switch (event.event) {\n case 'on_chat_model_start': {\n /**\n * End the previous model turn's reasoning stream before a new LLM invocation.\n * Without this, reasoning-delta chunks from the next turn could attach to the\n * wrong reasoning part in the UI message stream.\n */\n if (state.reasoningStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id:\n state.reasoningMessageId != null\n ? state.reasoningMessageId\n : state.messageId,\n });\n state.reasoningStarted = false;\n state.reasoningMessageId = null;\n }\n\n /**\n * End the previous model turn's text stream before a new LLM invocation.\n * The streamEvents adapter otherwise leaves `textStarted` true, so all later\n * text-delta chunks append to the first text part — tools still grow the parts\n * array, which breaks chronological order in the assistant message.\n */\n if (state.textStarted) {\n controller.enqueue({\n type: 'text-end',\n id:\n state.textMessageId != null ? state.textMessageId : state.messageId,\n });\n state.textStarted = false;\n state.textMessageId = null;\n }\n\n /**\n * Handle model start — capture message metadata if available.\n * `run_id` is on the event in streamEvents v2; fall back to `data` for compatibility.\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n if (runId) {\n state.messageId = runId;\n }\n break;\n }\n\n case 'on_chat_model_stream': {\n /**\n * Handle streaming token chunks\n */\n const chunk = event.data.chunk;\n if (chunk && typeof chunk === 'object') {\n /**\n * Get message ID from chunk if available\n */\n const chunkId = (chunk as { id?: string }).id;\n if (chunkId) {\n state.messageId = chunkId;\n }\n\n /**\n * Handle reasoning content from contentBlocks\n */\n const reasoning = extractReasoningFromContentBlocks(chunk);\n if (reasoning) {\n if (!state.reasoningStarted) {\n // Track the ID used for reasoning-start to ensure reasoning-end uses the same ID\n state.reasoningMessageId = state.messageId;\n controller.enqueue({\n type: 'reasoning-start',\n id: state.messageId,\n });\n state.reasoningStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: state.reasoningMessageId ?? state.messageId,\n });\n }\n\n /**\n * Extract text content from chunk\n */\n const content = (chunk as { content?: unknown }).content;\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n typeof c === 'object' &&\n c !== null &&\n 'type' in c &&\n c.type === 'text',\n )\n .map(c => c.text)\n .join('')\n : '';\n\n if (text) {\n /**\n * If reasoning was streamed before text, close reasoning first\n */\n if (state.reasoningStarted && !state.textStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: state.reasoningMessageId ?? state.messageId,\n });\n state.reasoningStarted = false;\n }\n\n if (!state.textStarted) {\n // Track the ID used for text-start to ensure text-end uses the same ID\n state.textMessageId = state.messageId;\n controller.enqueue({ type: 'text-start', id: state.messageId });\n state.textStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: state.textMessageId ?? state.messageId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(chunk);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n state.messageId,\n state.emittedSourceIds,\n controller,\n );\n }\n }\n break;\n }\n\n case 'on_tool_start': {\n /**\n * Handle tool call start\n * run_id and name are at event level in v2, check data for backwards compatibility\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n const name = event.name || (event.data.name as string | undefined);\n\n if (runId && name) {\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: runId,\n toolName: name,\n dynamic: true,\n });\n }\n break;\n }\n\n case 'on_tool_end': {\n /**\n * Handle tool call end\n * run_id is at event level in v2, check data for backwards compatibility\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n const output = event.data.output;\n\n if (runId) {\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId: runId,\n output,\n });\n }\n break;\n }\n }\n}\n\n/**\n * Checks if an error is an abort error.\n */\nfunction isAbortError(error: unknown): boolean {\n if (error instanceof Error) {\n return (\n error.name === 'AbortError' ||\n (error instanceof DOMException && error.name === 'AbortError')\n );\n }\n return false;\n}\n\n/**\n * Converts a LangChain stream to an AI SDK UIMessageStream.\n *\n * This function automatically detects the stream type and handles:\n * - Direct model streams (AsyncIterable from `model.stream()`)\n * - LangGraph streams (ReadableStream with `streamMode: ['values', 'messages']`)\n * - streamEvents streams (from `agent.streamEvents()` or `model.streamEvents()`)\n *\n * @param stream - A stream from LangChain model.stream(), graph.stream(), or streamEvents().\n * @param callbacks - Optional callbacks for stream lifecycle events.\n * @returns A ReadableStream of UIMessageChunk objects.\n *\n * @example\n * ```ts\n * // With a direct model stream\n * const model = new ChatOpenAI({ model: 'gpt-4o-mini' });\n * const stream = await model.stream(messages);\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(stream),\n * });\n *\n * // With a LangGraph stream\n * const graphStream = await graph.stream(\n * { messages },\n * { streamMode: ['values', 'messages'] }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(graphStream),\n * });\n *\n * // With streamEvents\n * const streamEvents = agent.streamEvents(\n * { messages },\n * { version: \"v2\" }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(streamEvents),\n * });\n *\n * // With callbacks for LangGraph state\n * const graphStream = await graph.stream(\n * { messages },\n * { streamMode: ['values', 'messages'] }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream<MyStateType>(graphStream, {\n * onFinish: async (finalState) => {\n * if (finalState) {\n * await saveToDatabase(finalState);\n * }\n * },\n * onError: (error) => console.error('Stream failed:', error),\n * onAbort: () => console.log('Stream aborted'),\n * }),\n * });\n * ```\n */\nexport function toUIMessageStream<TState = unknown>(\n stream: AsyncIterable<AIMessageChunk> | ReadableStream,\n callbacks?: StreamCallbacks<TState>,\n): ReadableStream<UIMessageChunk> {\n /**\n * Track text chunks for onFinal callback\n */\n const textChunks: string[] = [];\n\n /** Last LangGraph values event data for onFinish callback */\n let lastValuesData: TState | undefined;\n\n /**\n * State for model stream handling\n */\n const modelState = {\n started: false,\n messageId: 'langchain-msg-1',\n reasoningStarted: false,\n textStarted: false,\n /** Track the ID used for text-start to ensure text-end uses the same ID */\n textMessageId: null as string | null,\n /** Track the ID used for reasoning-start to ensure reasoning-end uses the same ID */\n reasoningMessageId: null as string | null,\n emittedSourceIds: new Set<string>(),\n };\n\n /**\n * State for LangGraph stream handling\n */\n const langGraphState: LangGraphEventState = {\n messageSeen: new Map(),\n messageConcat: new Map(),\n emittedToolCalls: new Set<string>(),\n emittedToolInputs: new Set<string>(),\n emittedImages: new Set<string>(),\n emittedReasoningIds: new Set<string>(),\n messageReasoningIds: new Map(),\n toolCallInfoByIndex: new Map(),\n currentStep: null as number | null,\n emittedToolCallsByKey: new Map<string, string>(),\n emittedSourceIds: new Set<string>(),\n };\n\n /**\n * Track detected stream type: null = not yet detected\n */\n let streamType: 'model' | 'langgraph' | 'streamEvents' | null = null;\n\n /**\n * Get async iterator from the stream (works for both AsyncIterable and ReadableStream)\n */\n const getAsyncIterator = (): AsyncIterator<unknown> => {\n if (Symbol.asyncIterator in stream) {\n return (stream as AsyncIterable<unknown>)[Symbol.asyncIterator]();\n }\n /**\n * For ReadableStream without Symbol.asyncIterator\n */\n const reader = (stream as ReadableStream).getReader();\n return {\n async next() {\n const { done, value } = await reader.read();\n return { done, value };\n },\n };\n };\n\n const iterator = getAsyncIterator();\n\n /**\n * Create a wrapper around the controller to intercept text chunks for callbacks\n */\n const createCallbackController = (\n originalController: ReadableStreamDefaultController<UIMessageChunk>,\n ): ReadableStreamDefaultController<UIMessageChunk> => {\n return {\n get desiredSize() {\n return originalController.desiredSize;\n },\n close: () => originalController.close(),\n error: (e?: unknown) => originalController.error(e),\n enqueue: (chunk: UIMessageChunk) => {\n /**\n * Intercept text-delta chunks for callbacks\n */\n if (callbacks && chunk.type === 'text-delta' && chunk.delta) {\n textChunks.push(chunk.delta);\n callbacks.onToken?.(chunk.delta);\n callbacks.onText?.(chunk.delta);\n }\n originalController.enqueue(chunk);\n },\n };\n };\n\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n await callbacks?.onStart?.();\n\n const wrappedController = createCallbackController(controller);\n controller.enqueue({ type: 'start' });\n\n try {\n while (true) {\n const { done, value } = await iterator.next();\n if (done) break;\n\n /**\n * Detect stream type on first value\n */\n if (streamType === null) {\n if (Array.isArray(value)) {\n streamType = 'langgraph';\n } else if (isStreamEventsEvent(value)) {\n streamType = 'streamEvents';\n } else {\n streamType = 'model';\n }\n }\n\n /**\n * Process based on detected type\n */\n if (streamType === 'model') {\n processModelChunk(\n value as AIMessageChunk,\n modelState,\n wrappedController,\n );\n } else if (streamType === 'streamEvents') {\n processStreamEventsEvent(\n value as {\n event: string;\n data: Record<string, unknown> | null;\n run_id?: string;\n name?: string;\n },\n modelState,\n wrappedController,\n );\n } else {\n const eventArray = value as unknown[];\n const [type, data] = parseLangGraphEvent(eventArray);\n\n if (type === 'values') {\n lastValuesData = data as TState;\n }\n\n processLangGraphEvent(\n eventArray,\n langGraphState,\n wrappedController,\n );\n }\n }\n\n /**\n * Finalize based on stream type\n */\n if (streamType === 'model' || streamType === 'streamEvents') {\n if (modelState.reasoningStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: modelState.reasoningMessageId ?? modelState.messageId,\n });\n }\n if (modelState.textStarted) {\n /**\n * Use the same ID that was used for text-start\n */\n controller.enqueue({\n type: 'text-end',\n id: modelState.textMessageId ?? modelState.messageId,\n });\n }\n controller.enqueue({ type: 'finish' });\n } else if (streamType === 'langgraph') {\n /**\n * Close any open text/reasoning parts before finishing.\n * This handles streams without values events (e.g. streamMode: 'messages')\n * where the values handler never ran to emit *-end events.\n */\n for (const [id, seen] of langGraphState.messageSeen) {\n if (seen.text) {\n controller.enqueue({ type: 'text-end', id });\n }\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n }\n\n /**\n * Emit finish-step if a step was started\n */\n if (langGraphState.currentStep !== null) {\n controller.enqueue({ type: 'finish-step' });\n }\n controller.enqueue({ type: 'finish' });\n }\n\n /**\n * Call onFinal callback with aggregated text\n */\n await callbacks?.onFinal?.(textChunks.join(''));\n await callbacks?.onFinish?.(lastValuesData);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n await callbacks?.onFinal?.(textChunks.join(''));\n\n if (isAbortError(error)) {\n await callbacks?.onAbort?.();\n } else {\n await callbacks?.onError?.(errorObj);\n }\n\n controller.enqueue({\n type: 'error',\n errorText: errorObj.message,\n });\n } finally {\n controller.close();\n }\n },\n });\n}\n","import {\n AIMessage,\n HumanMessage,\n ToolMessage,\n AIMessageChunk,\n type ContentBlock,\n type ToolCall,\n type BaseMessage,\n type BaseMessageChunk,\n type ToolCallChunk,\n} from '@langchain/core/messages';\nimport type {\n UIMessageChunk,\n ToolResultPart,\n AssistantContent,\n UserContent,\n ProviderMetadata,\n JSONValue,\n} from 'ai';\n\nimport type {\n LangGraphEventState,\n LangGraphMessageSeen,\n ReasoningContentBlock,\n ThinkingContentBlock,\n GPT5ReasoningOutput,\n ImageGenerationOutput,\n NormalizedCitation,\n} from './types';\n\n/**\n * Parses a LangGraph event tuple into [type, data].\n * Handles both 2-element [type, data] and 3-element [namespace, type, data] formats.\n *\n * @param event - The raw LangGraph event array.\n * @returns A tuple of [type, data].\n */\nexport function parseLangGraphEvent(\n event: unknown[],\n): [type: unknown, data: unknown] {\n return event.length === 3 ? [event[1], event[2]] : [event[0], event[1]];\n}\n\n/**\n * Converts a ToolResultPart to a LangChain ToolMessage\n * @param block - The ToolResultPart to convert.\n * @returns The converted ToolMessage.\n */\nexport function convertToolResultPart(block: ToolResultPart): ToolMessage {\n const content = (() => {\n if (block.output.type === 'text' || block.output.type === 'error-text') {\n return block.output.value;\n }\n\n if (block.output.type === 'json' || block.output.type === 'error-json') {\n return JSON.stringify(block.output.value);\n }\n\n if (block.output.type === 'content') {\n return block.output.value\n .map(outputBlock => {\n if (outputBlock.type === 'text') {\n return outputBlock.text;\n }\n return '';\n })\n .join('');\n }\n\n return '';\n })();\n\n return new ToolMessage({\n tool_call_id: block.toolCallId,\n content,\n });\n}\n\n/**\n * Converts AssistantContent to LangChain AIMessage\n * @param content - The AssistantContent to convert.\n * @returns The converted AIMessage.\n */\nexport function convertAssistantContent(content: AssistantContent): AIMessage {\n if (typeof content === 'string') {\n return new AIMessage({ content });\n }\n\n const textParts: string[] = [];\n const toolCalls: Array<{\n id: string;\n name: string;\n args: Record<string, unknown>;\n }> = [];\n\n for (const part of content) {\n if (part.type === 'text') {\n textParts.push(part.text);\n } else if (part.type === 'tool-call') {\n toolCalls.push({\n id: part.toolCallId,\n name: part.toolName,\n args: part.input as Record<string, unknown>,\n });\n }\n }\n\n return new AIMessage({\n content: textParts.join(''),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n });\n}\n\n/**\n * Helper to generate a default filename from mediaType\n */\nfunction getDefaultFilename(\n mediaType: string,\n prefix: string = 'file',\n): string {\n const fileExtension = mediaType.split('/')[1] || 'bin';\n return `${prefix}.${fileExtension}`;\n}\n\n/**\n * OpenAI-native content block type for images.\n * This format is passed through directly by ChatOpenAI to OpenAI's API.\n */\ntype OpenAIImageBlock = {\n type: 'image_url';\n image_url: {\n url: string;\n detail?: 'auto' | 'low' | 'high';\n };\n};\n\n/**\n * Content block type for HumanMessage that supports both text and OpenAI images.\n */\ntype HumanMessageContentBlock =\n | { type: 'text'; text: string }\n | OpenAIImageBlock\n | ContentBlock;\n\n/**\n * Converts UserContent to LangChain HumanMessage\n * @param content - The UserContent to convert.\n * @returns The converted HumanMessage.\n */\nexport function convertUserContent(content: UserContent): HumanMessage {\n if (typeof content === 'string') {\n return new HumanMessage({ content });\n }\n\n const contentBlocks: HumanMessageContentBlock[] = [];\n\n for (const part of content) {\n if (part.type === 'text') {\n contentBlocks.push({ type: 'text', text: part.text });\n } else if (part.type === 'image') {\n const imagePart = part as {\n type: 'image';\n image: string | Uint8Array | URL | ArrayBuffer;\n mediaType?: string;\n };\n\n /**\n * Use OpenAI's native image_url format which is passed through directly\n * handle URL objects\n */\n if (imagePart.image instanceof URL) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: imagePart.image.toString() },\n });\n } else if (typeof imagePart.image === 'string') {\n /**\n * Handle string (could be URL or base64)\n */\n /**\n * Check if it's a URL (including data: URLs)\n */\n if (\n imagePart.image.startsWith('http://') ||\n imagePart.image.startsWith('https://') ||\n imagePart.image.startsWith('data:')\n ) {\n /**\n * OpenAI accepts both http URLs and data URLs directly\n */\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: imagePart.image },\n });\n } else {\n /**\n * Assume base64 encoded data - wrap in data URL\n */\n const mimeType = imagePart.mediaType || 'image/png';\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${mimeType};base64,${imagePart.image}` },\n });\n }\n } else if (\n /**\n * Handle Uint8Array or ArrayBuffer (binary data)\n */\n imagePart.image instanceof Uint8Array ||\n imagePart.image instanceof ArrayBuffer\n ) {\n const bytes =\n imagePart.image instanceof ArrayBuffer\n ? new Uint8Array(imagePart.image)\n : imagePart.image;\n /**\n * Convert to base64 data URL\n */\n const base64 = btoa(String.fromCharCode(...bytes));\n const mimeType = imagePart.mediaType || 'image/png';\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${mimeType};base64,${base64}` },\n });\n }\n } else if (part.type === 'file') {\n const rawFilePart = part as {\n type: 'file';\n data:\n | string\n | Uint8Array\n | URL\n | ArrayBuffer\n | { type: 'data'; data: string | Uint8Array | ArrayBuffer }\n | { type: 'url'; url: URL }\n | { type: 'reference'; reference: Record<string, string> }\n | { type: 'text'; text: string };\n mediaType: string;\n filename?: string;\n };\n\n // Normalize tagged data shape into the legacy bare value this code expects.\n const normalizedData: string | Uint8Array | URL | ArrayBuffer = (() => {\n const data = rawFilePart.data;\n if (\n typeof data === 'object' &&\n data !== null &&\n !(data instanceof URL) &&\n !(data instanceof Uint8Array) &&\n !(data instanceof ArrayBuffer) &&\n 'type' in data\n ) {\n switch (data.type) {\n case 'data':\n return data.data;\n case 'url':\n return data.url;\n case 'text':\n return data.text;\n default:\n return '';\n }\n }\n return data as string | Uint8Array | URL | ArrayBuffer;\n })();\n\n const filePart = {\n type: 'file' as const,\n data: normalizedData,\n mediaType: rawFilePart.mediaType,\n filename: rawFilePart.filename,\n };\n\n /**\n * Check if this is an image file - if so, use OpenAI's image_url format\n */\n const isImage = filePart.mediaType?.startsWith('image/');\n\n if (isImage) {\n /**\n * Handle image files using OpenAI's native image_url format\n */\n if (filePart.data instanceof URL) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: filePart.data.toString() },\n });\n } else if (typeof filePart.data === 'string') {\n /**\n * URLs (including data URLs) can be passed directly\n */\n if (\n filePart.data.startsWith('http://') ||\n filePart.data.startsWith('https://') ||\n filePart.data.startsWith('data:')\n ) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: filePart.data },\n });\n } else {\n /**\n * Assume base64 - wrap in data URL\n */\n contentBlocks.push({\n type: 'image_url',\n image_url: {\n url: `data:${filePart.mediaType};base64,${filePart.data}`,\n },\n });\n }\n } else if (\n filePart.data instanceof Uint8Array ||\n filePart.data instanceof ArrayBuffer\n ) {\n const bytes =\n filePart.data instanceof ArrayBuffer\n ? new Uint8Array(filePart.data)\n : filePart.data;\n const base64 = btoa(String.fromCharCode(...bytes));\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${filePart.mediaType};base64,${base64}` },\n });\n }\n } else {\n // Handle non-image files using LangChain's ContentBlock format\n const filename =\n filePart.filename || getDefaultFilename(filePart.mediaType, 'file');\n\n if (filePart.data instanceof URL) {\n contentBlocks.push({\n type: 'file',\n url: filePart.data.toString(),\n mimeType: filePart.mediaType,\n filename,\n });\n } else if (typeof filePart.data === 'string') {\n if (\n filePart.data.startsWith('http://') ||\n filePart.data.startsWith('https://')\n ) {\n contentBlocks.push({\n type: 'file',\n url: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n } else if (filePart.data.startsWith('data:')) {\n const matches = filePart.data.match(/^data:([^;]+);base64,(.+)$/);\n if (matches) {\n contentBlocks.push({\n type: 'file',\n data: matches[2],\n mimeType: matches[1],\n filename,\n });\n } else {\n contentBlocks.push({\n type: 'file',\n url: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n } else {\n contentBlocks.push({\n type: 'file',\n data: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n } else if (\n filePart.data instanceof Uint8Array ||\n filePart.data instanceof ArrayBuffer\n ) {\n const bytes =\n filePart.data instanceof ArrayBuffer\n ? new Uint8Array(filePart.data)\n : filePart.data;\n const base64 = btoa(String.fromCharCode(...bytes));\n contentBlocks.push({\n type: 'file',\n data: base64,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n }\n }\n }\n\n /**\n * If we only have text parts, join them as a simple string for efficiency\n */\n if (contentBlocks.every(block => block.type === 'text')) {\n return new HumanMessage({\n content: contentBlocks\n .map(block => (block as unknown as { text: string }).text)\n .join(''),\n });\n }\n\n return new HumanMessage({ content: contentBlocks });\n}\n\n/**\n * Helper to check if a content item is a ToolResultPart\n * @param item - The item to check.\n * @returns True if the item is a ToolResultPart, false otherwise.\n */\nexport function isToolResultPart(item: unknown): item is ToolResultPart {\n return (\n item != null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as { type: string }).type === 'tool-result'\n );\n}\n\n/**\n * Processes a model stream chunk and emits UI message chunks.\n * @param chunk - The AIMessageChunk to process.\n * @param state - The state of the model stream.\n * @param controller - The controller to use to emit the UI message chunks.\n */\nexport function processModelChunk(\n chunk: AIMessageChunk,\n state: {\n started: boolean;\n messageId: string;\n reasoningStarted?: boolean;\n textStarted?: boolean;\n /** Track the ID used for reasoning-start to ensure reasoning-end uses the same ID */\n reasoningMessageId?: string | null;\n /** Track the ID used for text-start to ensure text-end uses the same ID */\n textMessageId?: string | null;\n emittedImages?: Set<string>;\n emittedSourceIds?: Set<string>;\n },\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n /**\n * Initialize emittedImages set if not present\n */\n if (!state.emittedImages) {\n state.emittedImages = new Set<string>();\n }\n\n if (!state.emittedSourceIds) {\n state.emittedSourceIds = new Set<string>();\n }\n\n /**\n * Get the message ID from the chunk if available\n */\n if (chunk.id) {\n state.messageId = chunk.id;\n }\n\n /**\n * Handle image generation outputs from additional_kwargs.tool_outputs\n */\n const chunkObj = chunk as unknown as Record<string, unknown>;\n const additionalKwargs = chunkObj.additional_kwargs as\n | Record<string, unknown>\n | undefined;\n const imageOutputs = extractImageOutputs(additionalKwargs);\n\n for (const imageOutput of imageOutputs) {\n /**\n * Only emit if we have image data and haven't emitted this image yet\n */\n if (imageOutput.result && !state.emittedImages.has(imageOutput.id)) {\n state.emittedImages.add(imageOutput.id);\n\n /**\n * Emit as a file part using proper AI SDK multimodal format\n */\n const mediaType = `image/${imageOutput.output_format || 'png'}`;\n controller.enqueue({\n type: 'file',\n mediaType,\n url: `data:${mediaType};base64,${imageOutput.result}`,\n });\n state.started = true;\n }\n }\n\n /**\n * Handle reasoning content from contentBlocks or response_metadata.output\n * For direct model streams, we check both sources since there's no values event\n * that would cause duplication (unlike LangGraph streams)\n */\n const reasoning =\n extractReasoningFromContentBlocks(chunk) ||\n extractReasoningFromValuesMessage(chunk);\n if (reasoning) {\n if (!state.reasoningStarted) {\n // Track the ID used for reasoning-start to ensure subsequent chunks use the same ID\n state.reasoningMessageId = state.messageId;\n controller.enqueue({ type: 'reasoning-start', id: state.messageId });\n state.reasoningStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: state.reasoningMessageId ?? state.messageId,\n });\n }\n\n /**\n * Extract text content from AIMessageChunk\n */\n const text =\n typeof chunk.content === 'string'\n ? chunk.content\n : Array.isArray(chunk.content)\n ? chunk.content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n typeof c === 'object' &&\n c !== null &&\n 'type' in c &&\n c.type === 'text',\n )\n .map(c => c.text)\n .join('')\n : '';\n\n if (text) {\n /**\n * If reasoning was streamed before text, close reasoning first\n */\n if (state.reasoningStarted && !state.textStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: state.reasoningMessageId ?? state.messageId,\n });\n state.reasoningStarted = false;\n }\n\n if (!state.textStarted) {\n // Track the ID used for text-start to ensure subsequent chunks use the same ID\n state.textMessageId = state.messageId;\n controller.enqueue({ type: 'text-start', id: state.messageId });\n state.textStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: state.textMessageId ?? state.messageId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(chunk);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n state.messageId,\n state.emittedSourceIds,\n controller,\n );\n }\n}\n\n/**\n * Checks if a message is a plain object (not a LangChain class instance).\n * LangChain class instances have a _getType method.\n *\n * @param msg - The message to check.\n * @returns True if the message is a plain object, false otherwise.\n */\nexport function isPlainMessageObject(msg: unknown): boolean {\n if (msg == null || typeof msg !== 'object') return false;\n /**\n * LangChain class instances have _getType method\n */\n return typeof (msg as { _getType?: unknown })._getType !== 'function';\n}\n\n/**\n * Extracts the actual message ID from a message.\n * Handles both class instances (msg.id) and serialized LangChain messages (msg.kwargs.id).\n *\n * @param msg - The message to extract the ID from.\n * @returns The message ID string, or undefined if not found.\n */\nexport function getMessageId(msg: unknown): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n const msgObj = msg as Record<string, unknown>;\n\n /**\n * For class instances, id is directly on the object\n */\n if (typeof msgObj.id === 'string') {\n return msgObj.id;\n }\n\n /**\n * For serialized LangChain messages, id is in kwargs\n */\n if (\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ) {\n const kwargs = msgObj.kwargs as Record<string, unknown>;\n if (typeof kwargs.id === 'string') {\n return kwargs.id;\n }\n }\n\n return undefined;\n}\n\n/**\n * Checks if a message is an AI message chunk (works for both class instances and plain objects).\n * For class instances, only AIMessageChunk is matched (not AIMessage).\n * For plain objects from RemoteGraph API, matches type === 'ai' (TypeScript langchain-core)\n * or type === 'AIMessageChunk' (Python langchain-core).\n * For serialized LangChain messages, matches type === 'constructor' with AIMessageChunk in id path.\n *\n * @param msg - The message to check.\n * @returns True if the message is an AI message chunk, false otherwise.\n */\nexport function isAIMessageChunk(\n msg: unknown,\n): msg is AIMessageChunk & { type?: string; content?: string } {\n /**\n * Actual AIMessageChunk class instance\n */\n if (AIMessageChunk.isInstance(msg)) return true;\n /**\n * Plain object from RemoteGraph API (not a LangChain class instance)\n */\n if (isPlainMessageObject(msg)) {\n const messageRecord = msg as Record<string, unknown>;\n /**\n * Direct type from RemoteGraph format. TypeScript langchain-core emits\n * type === 'ai'; Python langchain-core emits type === 'AIMessageChunk'.\n */\n if (\n 'type' in messageRecord &&\n (messageRecord.type === 'ai' || messageRecord.type === 'AIMessageChunk')\n ) {\n return true;\n }\n /**\n * Serialized LangChain message format: { lc: 1, type: \"constructor\", id: [\"...\", \"AIMessageChunk\"], kwargs: {...} }\n */\n if (\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n (messageRecord.id.includes('AIMessageChunk') ||\n messageRecord.id.includes('AIMessage'))\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Checks if a message is a Tool message (works for both class instances and plain objects).\n *\n * @param msg - The message to check.\n * @returns True if the message is a Tool message, false otherwise.\n */\nexport function isToolMessageType(\n msg: unknown,\n): msg is ToolMessage & { type?: string; tool_call_id?: string } {\n if (ToolMessage.isInstance(msg)) return true;\n /**\n * Plain object from RemoteGraph API (not a LangChain class instance)\n */\n if (isPlainMessageObject(msg)) {\n const messageRecord = msg as Record<string, unknown>;\n /**\n * Direct type === 'tool' (RemoteGraph format)\n */\n if ('type' in messageRecord && messageRecord.type === 'tool') return true;\n /**\n * Serialized LangChain message format\n */\n if (\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n messageRecord.id.includes('ToolMessage')\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Gets text content from a message (works for both class instances and plain objects).\n *\n * @param msg - The message to get the text from.\n * @returns The text content of the message.\n */\nexport function getMessageText(msg: unknown): string {\n if (AIMessageChunk.isInstance(msg)) {\n return msg.text ?? '';\n }\n\n if (msg == null || typeof msg !== 'object') return '';\n\n const msgObj = msg as Record<string, unknown>;\n\n // For serialized LangChain messages, content is in kwargs\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n if ('content' in dataSource) {\n const content = dataSource.content;\n /**\n * Handle string content\n */\n if (typeof content === 'string') {\n return content;\n }\n /**\n * Handle array of content blocks (e.g., [{ type: 'text', text: 'The', index: 0 }])\n */\n if (Array.isArray(content)) {\n return content\n .filter(\n (block): block is { type: 'text'; text: string } =>\n block != null &&\n typeof block === 'object' &&\n block.type === 'text' &&\n typeof block.text === 'string',\n )\n .map(block => block.text)\n .join('');\n }\n return '';\n }\n return '';\n}\n\n/**\n * Checks if an object is a reasoning content block\n *\n * @param obj - The object to check.\n * @returns True if the object is a reasoning content block, false otherwise.\n */\nexport function isReasoningContentBlock(\n obj: unknown,\n): obj is ReasoningContentBlock {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'reasoning' &&\n 'reasoning' in obj &&\n typeof (obj as { reasoning: unknown }).reasoning === 'string'\n );\n}\n\n/**\n * Checks if an object is a thinking content block (Anthropic-style)\n *\n * @param obj - The object to check.\n * @returns True if the object is a thinking content block, false otherwise.\n */\nexport function isThinkingContentBlock(\n obj: unknown,\n): obj is ThinkingContentBlock {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'thinking' &&\n 'thinking' in obj &&\n typeof (obj as { thinking: unknown }).thinking === 'string'\n );\n}\n\n/**\n * Checks if an object is a GPT-5 reasoning output block\n */\nfunction isGPT5ReasoningOutput(obj: unknown): obj is GPT5ReasoningOutput {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'reasoning' &&\n 'summary' in obj &&\n Array.isArray((obj as { summary: unknown }).summary)\n );\n}\n\n/**\n * Extracts the reasoning block ID from a message (GPT-5 format).\n * This ID is consistent across streaming and values events.\n * Handles both class instances and serialized LangChain message objects.\n *\n * @param msg - The message to extract the reasoning ID from.\n * @returns The reasoning block ID if found, undefined otherwise.\n */\nexport function extractReasoningId(msg: unknown): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check additional_kwargs.reasoning.id (GPT-5 streaming format)\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { id?: string } } }\n ).additional_kwargs;\n if (additionalKwargs?.reasoning?.id) {\n return additionalKwargs.reasoning.id;\n }\n\n // Check response_metadata.output for reasoning block ID (GPT-5 final format)\n const responseMetadata = (\n kwargs as { response_metadata?: { output?: unknown[] } }\n ).response_metadata;\n if (responseMetadata && Array.isArray(responseMetadata.output)) {\n for (const item of responseMetadata.output) {\n if (isGPT5ReasoningOutput(item)) {\n return item.id;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Extracts reasoning content from contentBlocks or additional_kwargs.reasoning.summary\n *\n * IMPORTANT: This function is designed for STREAMING chunks where content is delta-based.\n * It does NOT extract from response_metadata.output because that contains accumulated\n * content (not deltas) and would cause duplication during streaming.\n *\n * For non-streaming/values events, use extractReasoningFromValuesMessage instead.\n *\n * Handles both class instances and serialized LangChain message objects.\n *\n * @param msg - The message to extract reasoning from.\n * @returns The reasoning text if found, undefined otherwise.\n */\nexport function extractReasoningFromContentBlocks(\n msg: unknown,\n): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check contentBlocks (Anthropic-style) - highest priority\n const contentBlocks = (kwargs as { contentBlocks?: unknown[] }).contentBlocks;\n if (Array.isArray(contentBlocks)) {\n const reasoningParts: string[] = [];\n for (const block of contentBlocks) {\n if (isReasoningContentBlock(block)) {\n reasoningParts.push(block.reasoning);\n } else if (isThinkingContentBlock(block)) {\n reasoningParts.push(block.thinking);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n // Check additional_kwargs.reasoning.summary (GPT-5 streaming format)\n // This contains DELTA content during streaming, not accumulated content\n // Format can be either { type: \"summary_text\", text: \"...\" } or just { text: \"...\" }\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { summary?: unknown[] } } }\n ).additional_kwargs;\n if (\n additionalKwargs?.reasoning &&\n Array.isArray(additionalKwargs.reasoning.summary)\n ) {\n const reasoningParts: string[] = [];\n for (const summaryItem of additionalKwargs.reasoning.summary) {\n if (\n typeof summaryItem === 'object' &&\n summaryItem !== null &&\n 'text' in summaryItem &&\n typeof (summaryItem as { text: unknown }).text === 'string'\n ) {\n reasoningParts.push((summaryItem as { text: string }).text);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n return undefined;\n}\n\n/**\n * Extracts reasoning content from a values event message.\n * This checks response_metadata.output which contains the full accumulated reasoning.\n *\n * @param msg - The message to extract reasoning from.\n * @returns The reasoning text if found, undefined otherwise.\n */\nexport function extractReasoningFromValuesMessage(\n msg: unknown,\n): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check response_metadata.output (GPT-5 final style) - for values events\n const responseMetadata = (\n kwargs as { response_metadata?: { output?: unknown[] } }\n ).response_metadata;\n if (responseMetadata && Array.isArray(responseMetadata.output)) {\n const reasoningParts: string[] = [];\n for (const item of responseMetadata.output) {\n if (isGPT5ReasoningOutput(item)) {\n // Extract text from summary array - handles both { type: \"summary_text\", text } and { text } formats\n for (const summaryItem of item.summary) {\n if (typeof summaryItem === 'object' && summaryItem !== null) {\n const text = (summaryItem as { text?: string }).text;\n if (typeof text === 'string' && text) {\n reasoningParts.push(text);\n }\n }\n }\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n // Also check additional_kwargs.reasoning.summary as fallback\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { summary?: unknown[] } } }\n ).additional_kwargs;\n if (\n additionalKwargs?.reasoning &&\n Array.isArray(additionalKwargs.reasoning.summary)\n ) {\n const reasoningParts: string[] = [];\n for (const summaryItem of additionalKwargs.reasoning.summary) {\n if (\n typeof summaryItem === 'object' &&\n summaryItem !== null &&\n 'text' in summaryItem &&\n typeof (summaryItem as { text: unknown }).text === 'string'\n ) {\n reasoningParts.push((summaryItem as { text: string }).text);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n return undefined;\n}\n\nexport function isCitationContentBlock(\n obj: unknown,\n): obj is ContentBlock.Citation {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: unknown }).type === 'citation'\n );\n}\n\n/**\n * LangChain Core standardizes citations as `{ type: 'citation', ... }` entries in\n * the `annotations` array of a text content block. The text extractors elsewhere\n * in this file only read the `text` field, so without this the citations would be\n * dropped instead of surfaced as AI SDK source parts.\n *\n * Handles both class instances and serialized LangChain message objects.\n */\nexport function extractCitationsFromContentBlocks(\n msg: unknown,\n): NormalizedCitation[] {\n if (msg == null || typeof msg !== 'object') return [];\n\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // `contentBlocks` is the normalized view derived from `content`; reading both\n // would double-count annotations, so prefer it and only fall back to `content`.\n const contentBlocks = (kwargs as { contentBlocks?: unknown }).contentBlocks;\n const content = (kwargs as { content?: unknown }).content;\n const blockSources: unknown[] = Array.isArray(contentBlocks)\n ? contentBlocks\n : Array.isArray(content)\n ? content\n : [];\n\n const citations: NormalizedCitation[] = [];\n\n for (const block of blockSources) {\n if (block == null || typeof block !== 'object') continue;\n\n const annotations = (block as { annotations?: unknown }).annotations;\n if (!Array.isArray(annotations)) continue;\n\n for (const annotation of annotations) {\n if (!isCitationContentBlock(annotation)) continue;\n\n citations.push({\n url: annotation.url,\n title: annotation.title,\n source: annotation.source,\n citedText: annotation.citedText,\n startIndex: annotation.startIndex,\n endIndex: annotation.endIndex,\n });\n }\n }\n\n return citations;\n}\n\nfunction buildSourceProviderMetadata(\n citation: NormalizedCitation,\n): ProviderMetadata | undefined {\n const langchain: Record<string, JSONValue> = {};\n\n if (typeof citation.citedText === 'string') {\n langchain.citedText = citation.citedText;\n }\n if (typeof citation.startIndex === 'number') {\n langchain.startIndex = citation.startIndex;\n }\n if (typeof citation.endIndex === 'number') {\n langchain.endIndex = citation.endIndex;\n }\n if (typeof citation.source === 'string') {\n langchain.source = citation.source;\n }\n\n if (Object.keys(langchain).length === 0) {\n return undefined;\n }\n\n return { langchain };\n}\n\n/**\n * Emits AI SDK source chunks (`source-url` / `source-document`) for the given\n * citations.\n *\n * Citations with a `url` become `source-url` parts keyed by that url; the rest\n * become `source-document` parts. Citation metadata that the source parts cannot\n * represent natively (`citedText`, `startIndex`, `endIndex`, `source`) is preserved\n * under `providerMetadata.langchain`.\n *\n * `emittedSourceIds` dedupes across the LangGraph messages and values events, which\n * can each surface the same citation. URL-less citations are keyed by their content\n * rather than position, so a differing citation subset/order between those two events\n * does not cause an id collision.\n */\nexport function emitSourceChunks(\n citations: NormalizedCitation[],\n messageId: string,\n emittedSourceIds: Set<string>,\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n for (const citation of citations) {\n if (citation.url) {\n if (emittedSourceIds.has(citation.url)) continue;\n emittedSourceIds.add(citation.url);\n\n const providerMetadata = buildSourceProviderMetadata(citation);\n controller.enqueue({\n type: 'source-url',\n sourceId: citation.url,\n url: citation.url,\n ...(citation.title ? { title: citation.title } : {}),\n ...(providerMetadata ? { providerMetadata } : {}),\n });\n continue;\n }\n\n // A citation with no url and no human-readable label carries no information a\n // UI could render, so skip it rather than emit a placeholder source.\n const title = citation.title ?? citation.source;\n if (!title) continue;\n\n const sourceId = `${messageId}:${title}:${citation.citedText ?? ''}:${citation.startIndex ?? ''}:${citation.endIndex ?? ''}`;\n if (emittedSourceIds.has(sourceId)) continue;\n emittedSourceIds.add(sourceId);\n\n const providerMetadata = buildSourceProviderMetadata(citation);\n controller.enqueue({\n type: 'source-document',\n sourceId,\n mediaType: 'text/plain',\n title,\n ...(providerMetadata ? { providerMetadata } : {}),\n });\n }\n}\n\n/**\n * Checks if an object is an image generation output\n *\n * @param obj - The object to check.\n * @returns True if the object is an image generation output, false otherwise.\n */\nexport function isImageGenerationOutput(\n obj: unknown,\n): obj is ImageGenerationOutput {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'image_generation_call'\n );\n}\n\n/**\n * Extracts image generation outputs from `additional_kwargs`\n *\n * @param additionalKwargs - The additional kwargs to extract the image generation outputs from.\n * @returns The image generation outputs.\n */\nexport function extractImageOutputs(\n additionalKwargs: Record<string, unknown> | undefined,\n): ImageGenerationOutput[] {\n if (!additionalKwargs) return [];\n\n const toolOutputs = additionalKwargs.tool_outputs;\n if (!Array.isArray(toolOutputs)) return [];\n\n return toolOutputs.filter(isImageGenerationOutput);\n}\n\nfunction formatToolError(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n\n try {\n const serialized = JSON.stringify(error);\n return serialized ?? String(error);\n } catch {\n return String(error);\n }\n}\n\n/**\n * Returns per-message bookkeeping, creating it on first use.\n * Map keys keep remote-controlled message IDs like \"__proto__\" from touching Object.prototype.\n */\nfunction getOrCreateMessageSeen(\n messageSeen: LangGraphEventState['messageSeen'],\n msgId: string,\n): LangGraphMessageSeen {\n let seen = messageSeen.get(msgId);\n if (!seen) {\n seen = {};\n messageSeen.set(msgId, seen);\n }\n return seen;\n}\n\n/**\n * Returns the per-index tool call metadata for a message, creating it on first use.\n * Later streamed chunks use this to recover tool call IDs/names from earlier chunks.\n */\nfunction getOrCreateToolCallInfoByIndex(\n toolCallInfoByIndex: LangGraphEventState['toolCallInfoByIndex'],\n msgId: string,\n): Map<number, { id: string; name: string }> {\n let toolCallInfo = toolCallInfoByIndex.get(msgId);\n if (!toolCallInfo) {\n toolCallInfo = new Map();\n toolCallInfoByIndex.set(msgId, toolCallInfo);\n }\n return toolCallInfo;\n}\n\n/**\n * Processes a LangGraph event and emits UI message chunks.\n *\n * @param event - The event to process.\n * @param state - The state of the LangGraph event.\n * @param controller - The controller to use to emit the UI message chunks.\n */\nexport function processLangGraphEvent(\n event: unknown[],\n state: LangGraphEventState,\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n const {\n messageSeen,\n messageConcat,\n emittedToolCalls,\n emittedImages,\n emittedReasoningIds,\n messageReasoningIds,\n toolCallInfoByIndex,\n emittedToolCallsByKey,\n emittedToolInputs,\n } = state;\n const [type, data] = parseLangGraphEvent(event);\n\n switch (type) {\n case 'custom': {\n /**\n * Extract custom event type from the data's 'type' field if present.\n * This allows users to emit custom events like:\n * writer({ type: 'progress', value: 50 }) -> { type: 'data-progress', data: {...} }\n * writer({ type: 'status', message: '...' }) -> { type: 'data-status', data: {...} }\n * writer({ key: 'value' }) -> { type: 'data-custom', data: {...} } (fallback)\n *\n * The 'id' field can be used to make parts persistent and updateable.\n * Parts with an 'id' are NOT transient (added to message.parts).\n * Parts without an 'id' are transient (only passed to onData callback).\n */\n let customTypeName = 'custom';\n let partId: string | undefined;\n\n if (data != null && typeof data === 'object' && !Array.isArray(data)) {\n const dataObj = data as Record<string, unknown>;\n if (typeof dataObj.type === 'string' && dataObj.type) {\n customTypeName = dataObj.type;\n }\n if (typeof dataObj.id === 'string' && dataObj.id) {\n partId = dataObj.id;\n }\n }\n\n controller.enqueue({\n type: `data-${customTypeName}` as `data-${string}`,\n id: partId,\n transient: partId == null,\n data,\n });\n break;\n }\n\n case 'messages': {\n const [rawMsg, metadata] = data as [\n BaseMessageChunk | BaseMessage | undefined,\n Record<string, unknown> | undefined,\n ];\n\n const msg = rawMsg;\n const msgId = getMessageId(msg);\n\n if (!msgId) return;\n\n /**\n * Track LangGraph step changes and emit start-step/finish-step events.\n * Before emitting finish-step, close any open text/reasoning parts so\n * the client does not receive orphaned deltas after its\n * activeReasoningParts / activeTextParts have been cleared.\n */\n const langgraphStep =\n typeof metadata?.langgraph_step === 'number'\n ? metadata.langgraph_step\n : null;\n if (langgraphStep !== null && langgraphStep !== state.currentStep) {\n if (state.currentStep !== null) {\n for (const [id, seen] of messageSeen) {\n if (seen.text) {\n controller.enqueue({ type: 'text-end', id });\n }\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n messageSeen.delete(id);\n messageConcat.delete(id);\n messageReasoningIds.delete(id);\n }\n controller.enqueue({ type: 'finish-step' });\n }\n controller.enqueue({ type: 'start-step' });\n state.currentStep = langgraphStep;\n }\n\n /**\n * Accumulate message chunks for later reference\n * Note: Only works for actual class instances, not serialized messages\n */\n if (AIMessageChunk.isInstance(msg)) {\n const existingMessage = messageConcat.get(msgId);\n if (existingMessage) {\n messageConcat.set(\n msgId,\n existingMessage.concat(msg) as AIMessageChunk,\n );\n } else {\n messageConcat.set(msgId, msg);\n }\n }\n\n if (isAIMessageChunk(msg)) {\n const concatChunk = messageConcat.get(msgId);\n\n /**\n * Handle image generation outputs from additional_kwargs.tool_outputs\n * Handle both direct properties and serialized messages (kwargs)\n */\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n const additionalKwargs = dataSource.additional_kwargs as\n | Record<string, unknown>\n | undefined;\n const imageOutputs = extractImageOutputs(additionalKwargs);\n\n for (const imageOutput of imageOutputs) {\n /**\n * Only emit if we have image data and haven't emitted this image yet\n */\n if (imageOutput.result && !emittedImages.has(imageOutput.id)) {\n emittedImages.add(imageOutput.id);\n\n /**\n * Emit as a file part using proper AI SDK multimodal format\n */\n const mediaType = `image/${imageOutput.output_format || 'png'}`;\n controller.enqueue({\n type: 'file',\n mediaType,\n url: `data:${mediaType};base64,${imageOutput.result}`,\n });\n }\n }\n\n /**\n * Handle tool call chunks for streaming tool calls\n * Access from dataSource to handle both direct and serialized messages\n *\n * Tool call chunks are streamed as follows:\n * 1. First chunk: has name, id, but often empty args\n * 2. Subsequent chunks: have args but NO id or name\n *\n * We store tool call info by index when we first see it, then look it up\n * for subsequent chunks that don't include the id.\n */\n const toolCallChunks = dataSource.tool_call_chunks as\n | ToolCallChunk[]\n | undefined;\n if (toolCallChunks?.length) {\n for (const toolCallChunk of toolCallChunks) {\n const toolCallIndex = toolCallChunk.index ?? 0;\n\n /**\n * If this chunk has an id, store it for future lookups by index\n */\n if (toolCallChunk.id) {\n getOrCreateToolCallInfoByIndex(toolCallInfoByIndex, msgId).set(\n toolCallIndex,\n {\n id: toolCallChunk.id,\n name:\n toolCallChunk.name ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.name ||\n 'unknown',\n },\n );\n }\n\n /**\n * Get the tool call ID from the chunk, stored info, or accumulated chunks\n */\n const storedToolCallInfo = toolCallInfoByIndex\n .get(msgId)\n ?.get(toolCallIndex);\n const toolCallId =\n toolCallChunk.id ||\n storedToolCallInfo?.id ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.id;\n\n /**\n * Skip if we don't have a proper tool call ID - we'll handle it in values\n */\n if (!toolCallId) {\n continue;\n }\n\n const toolName =\n toolCallChunk.name ||\n storedToolCallInfo?.name ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.name ||\n 'unknown';\n\n /**\n * Emit tool-input-start when we first see this tool call\n * (even if args is empty - the first chunk often has empty args)\n * Set dynamic: true to enable HITL approval requests\n */\n const seen = messageSeen.get(msgId);\n if (!seen?.tool?.has(toolCallId)) {\n const updatedSeen = getOrCreateMessageSeen(messageSeen, msgId);\n updatedSeen.tool ??= new Set();\n updatedSeen.tool.add(toolCallId);\n\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: toolCallId,\n toolName: toolName,\n dynamic: true,\n });\n }\n }\n\n /**\n * Only emit tool-input-delta when args is non-empty\n */\n if (toolCallChunk.args) {\n controller.enqueue({\n type: 'tool-input-delta',\n toolCallId: toolCallId,\n inputTextDelta: toolCallChunk.args,\n });\n }\n }\n\n return;\n }\n\n /**\n * Handle reasoning content from contentBlocks\n * Streaming chunks contain DELTA text (not accumulated), so emit directly.\n * Use reasoning block ID for deduplication as it's consistent across streaming and values events.\n *\n * Important: Early chunks may have reasoning ID but no content, later chunks may\n * have content but no reasoning ID. We capture the ID when first seen and reuse it.\n * We also immediately add to emittedReasoningIds to prevent values events from\n * emitting the same reasoning (values events can arrive between streaming chunks).\n */\n // Capture reasoning ID when we first see it (even if no content yet)\n const chunkReasoningId = extractReasoningId(msg);\n if (chunkReasoningId) {\n if (!messageReasoningIds.has(msgId)) {\n messageReasoningIds.set(msgId, chunkReasoningId);\n }\n // Immediately mark as emitted to prevent values from duplicating\n // This must happen as soon as we see the ID, before content arrives\n emittedReasoningIds.add(chunkReasoningId);\n }\n\n const reasoning = extractReasoningFromContentBlocks(msg);\n if (reasoning) {\n // Use stored reasoning ID, or current chunk's ID, or fall back to message ID\n const reasoningId =\n messageReasoningIds.get(msgId) ?? chunkReasoningId ?? msgId;\n\n const seen = messageSeen.get(msgId);\n if (!seen?.reasoning) {\n controller.enqueue({ type: 'reasoning-start', id: msgId });\n getOrCreateMessageSeen(messageSeen, msgId).reasoning = true;\n }\n\n // Streaming chunks have delta text, emit directly without slicing\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: msgId,\n });\n // Also ensure the reasoning ID is marked (handles case where ID wasn't in first chunk)\n emittedReasoningIds.add(reasoningId);\n }\n\n /**\n * Handle text content\n */\n const text = getMessageText(msg);\n if (text) {\n const seen = messageSeen.get(msgId);\n if (!seen?.text) {\n controller.enqueue({ type: 'text-start', id: msgId });\n getOrCreateMessageSeen(messageSeen, msgId).text = true;\n }\n\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: msgId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(msg);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n msgId,\n state.emittedSourceIds,\n controller,\n );\n }\n } else if (isToolMessageType(msg)) {\n // Handle both direct properties and serialized messages (kwargs)\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n const toolCallId = dataSource.tool_call_id as string | undefined;\n const status = dataSource.status as string | undefined;\n\n if (toolCallId) {\n if (status === 'error') {\n // Tool execution failed\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId,\n errorText:\n typeof dataSource.content === 'string'\n ? dataSource.content\n : 'Tool execution failed',\n });\n } else {\n // Tool execution succeeded\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: dataSource.content,\n });\n }\n }\n }\n\n return;\n }\n\n case 'tools': {\n if (data == null || typeof data !== 'object' || Array.isArray(data)) {\n return;\n }\n\n const payload = data as {\n event?: unknown;\n name?: unknown;\n input?: unknown;\n data?: unknown;\n output?: unknown;\n error?: unknown;\n toolCallId?: unknown;\n };\n const toolCallId =\n typeof payload.toolCallId === 'string' ? payload.toolCallId : undefined;\n const toolName =\n typeof payload.name === 'string' ? payload.name : 'unknown';\n\n if (!toolCallId) return;\n\n const ensureToolInputLifecycle = () => {\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId,\n toolName,\n dynamic: true,\n });\n }\n\n if (!emittedToolInputs.has(toolCallId)) {\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input: payload.input,\n dynamic: true,\n });\n }\n };\n\n switch (payload.event) {\n case 'on_tool_start': {\n const toolCallKey = `${toolName}:${JSON.stringify(payload.input)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n\n ensureToolInputLifecycle();\n break;\n }\n\n case 'on_tool_event': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: payload.data,\n preliminary: true,\n });\n break;\n }\n\n case 'on_tool_end': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: payload.output,\n });\n break;\n }\n\n case 'on_tool_error': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId,\n errorText: formatToolError(payload.error),\n });\n break;\n }\n }\n\n return;\n }\n\n case 'values': {\n /**\n * Finalize all pending message chunks\n */\n for (const [id, seen] of messageSeen) {\n if (seen.text) controller.enqueue({ type: 'text-end', id });\n if (seen.tool) {\n for (const toolCallId of seen.tool) {\n const concatMsg = messageConcat.get(id);\n const toolCall = concatMsg?.tool_calls?.find(\n call => call.id === toolCallId,\n );\n\n if (toolCall) {\n emittedToolCalls.add(toolCallId);\n // Store mapping for HITL interrupt lookup\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n if (!emittedToolInputs.has(toolCallId)) {\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName: toolCall.name,\n input: toolCall.args,\n dynamic: true,\n });\n }\n }\n }\n }\n\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n\n messageSeen.delete(id);\n messageConcat.delete(id);\n messageReasoningIds.delete(id);\n }\n\n /**\n * Also check for tool calls in the final state that weren't streamed\n * This handles cases where tool calls appear directly in values without being in messages events\n */\n if (data != null && typeof data === 'object' && 'messages' in data) {\n const messages = (data as { messages?: unknown[] }).messages;\n if (Array.isArray(messages)) {\n /**\n * First pass: Collect all tool call IDs that have been responded to by ToolMessages.\n * These are historical tool calls that are already complete.\n */\n const completedToolCallIds = new Set<string>();\n for (const msg of messages) {\n if (!msg || typeof msg !== 'object') continue;\n\n if (isToolMessageType(msg)) {\n // Handle both direct properties and serialized messages (kwargs)\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n const toolCallId = dataSource.tool_call_id as string | undefined;\n if (toolCallId) {\n completedToolCallIds.add(toolCallId);\n }\n }\n }\n\n /**\n * Second pass: Process messages and emit tool events only for NEW tool calls\n * (those not already completed by a ToolMessage in the history)\n */\n for (const msg of messages) {\n if (!msg || typeof msg !== 'object') continue;\n\n // Use getMessageId to handle both class instances and serialized messages\n const msgId = getMessageId(msg);\n if (!msgId) continue;\n\n /**\n * Check if this is an AI message with tool calls\n */\n let toolCalls: ToolCall[] | undefined;\n\n /**\n * For class instances\n */\n if (AIMessageChunk.isInstance(msg) || AIMessage.isInstance(msg)) {\n toolCalls = msg.tool_calls;\n } else if (isPlainMessageObject(msg)) {\n /**\n * For plain objects from RemoteGraph API or serialized LangChain messages\n */\n const messageRecord = msg as Record<string, unknown>;\n\n /**\n * Determine the data source (handle both direct and serialized formats)\n */\n const isSerializedFormat =\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n ((messageRecord.id as string[]).includes('AIMessageChunk') ||\n (messageRecord.id as string[]).includes('AIMessage'));\n const dataSource = isSerializedFormat\n ? (messageRecord.kwargs as Record<string, unknown>)\n : messageRecord;\n\n if (\n messageRecord.type === 'ai' ||\n messageRecord.type === 'AIMessageChunk' ||\n isSerializedFormat\n ) {\n /**\n * Try tool_calls first (normalized format)\n */\n if (Array.isArray(dataSource?.tool_calls)) {\n toolCalls = dataSource.tool_calls as ToolCall[];\n } else if (\n /**\n * Fall back to additional_kwargs.tool_calls (OpenAI format)\n */\n dataSource?.additional_kwargs &&\n typeof dataSource.additional_kwargs === 'object'\n ) {\n const additionalKwargs =\n dataSource.additional_kwargs as Record<string, unknown>;\n if (Array.isArray(additionalKwargs.tool_calls)) {\n /**\n * Convert OpenAI format to normalized format\n */\n toolCalls = (\n additionalKwargs.tool_calls as Array<{\n id?: string;\n function?: { name?: string; arguments?: string };\n }>\n ).map((tc, idx) => {\n const functionData = tc.function;\n let args: unknown;\n try {\n args = functionData?.arguments\n ? JSON.parse(functionData.arguments)\n : {};\n } catch {\n args = {};\n }\n return {\n id: tc.id || `call_${idx}`,\n name: functionData?.name || 'unknown',\n args,\n } as ToolCall;\n });\n }\n }\n }\n }\n\n if (toolCalls && toolCalls.length > 0) {\n for (const toolCall of toolCalls) {\n /**\n * Only emit if we haven't already processed this tool call\n * AND if it's not a historical tool call that already has a ToolMessage response.\n * Historical completed tool calls should not be re-emitted as this would create\n * orphaned tool parts in the UI without corresponding outputs.\n */\n if (\n toolCall.id &&\n !emittedToolCalls.has(toolCall.id) &&\n !completedToolCallIds.has(toolCall.id)\n ) {\n emittedToolCalls.add(toolCall.id);\n // Store mapping for HITL interrupt lookup\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCall.id);\n /**\n * Emit tool-input-start first to ensure proper lifecycle.\n * Tool calls that weren't streamed (no tool_call_chunks) need\n * the start event before tool-input-available.\n */\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n dynamic: true,\n });\n emittedToolInputs.add(toolCall.id);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n input: toolCall.args,\n dynamic: true,\n });\n } else if (toolCall.id && emittedToolCalls.has(toolCall.id)) {\n // Register key mapping for tool calls already emitted via messages mode\n // so that __interrupt__ handling can match them by key\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCall.id);\n }\n }\n }\n\n /**\n * Check for reasoning content that wasn't streamed\n * Use reasoning block ID for deduplication as it's consistent across streaming and values.\n *\n * IMPORTANT: Handle two cases differently:\n * 1. Message has reasoning WITHOUT tool_calls → emit reasoning (pure reasoning case)\n * 2. Message has reasoning WITH tool_calls → only emit if streamed this request\n * (When resuming from HITL interrupt, historical messages have both reasoning\n * AND tool_calls. We skip those to avoid duplicate reasoning entries.)\n */\n const reasoningId = extractReasoningId(msg);\n const wasStreamedThisRequest = messageSeen.has(msgId);\n const hasToolCalls = toolCalls && toolCalls.length > 0;\n\n /**\n * Determine if we should emit reasoning:\n * - If we already emitted this reasoning ID, skip\n * - If the message was streamed this request, emit (normal flow)\n * - If NOT streamed but has NO tool_calls, emit (pure reasoning in values case)\n * - If NOT streamed but HAS tool_calls, skip (historical HITL message)\n */\n const shouldEmitReasoning =\n reasoningId &&\n !emittedReasoningIds.has(reasoningId) &&\n (wasStreamedThisRequest || !hasToolCalls);\n\n if (shouldEmitReasoning) {\n /**\n * Use extractReasoningFromValuesMessage which extracts from response_metadata.output\n * This is the full accumulated reasoning, not deltas\n */\n const reasoning = extractReasoningFromValuesMessage(msg);\n\n if (reasoning) {\n controller.enqueue({ type: 'reasoning-start', id: msgId });\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: msgId,\n });\n controller.enqueue({ type: 'reasoning-end', id: msgId });\n emittedReasoningIds.add(reasoningId);\n }\n }\n\n const valuesCitations = extractCitationsFromContentBlocks(msg);\n if (valuesCitations.length > 0) {\n emitSourceChunks(\n valuesCitations,\n msgId,\n state.emittedSourceIds,\n controller,\n );\n }\n }\n }\n }\n\n /**\n * Handle Human-in-the-Loop interrupts\n * When HITL middleware pauses execution, the interrupt data is in __interrupt__\n * Note: This is outside the 'messages' check because interrupt can come as a separate event\n */\n if (data != null && typeof data === 'object') {\n const interrupt = (data as Record<string, unknown>).__interrupt__;\n if (Array.isArray(interrupt) && interrupt.length > 0) {\n for (const interruptItem of interrupt) {\n const interruptValue = (interruptItem as { value?: unknown })\n ?.value as Record<string, unknown> | undefined;\n\n if (!interruptValue) continue;\n\n /**\n * Support both camelCase (JS SDK) and snake_case (Python SDK)\n */\n const actionRequests = (interruptValue.actionRequests ||\n interruptValue.action_requests) as\n | Array<{\n name: string;\n args?: Record<string, unknown>; // JS SDK uses 'args'\n arguments?: Record<string, unknown>; // Python SDK uses 'arguments'\n id?: string;\n }>\n | undefined;\n\n if (!Array.isArray(actionRequests)) continue;\n\n for (const actionRequest of actionRequests) {\n const toolName = actionRequest.name;\n /**\n * Support both 'args' (JS SDK) and 'arguments' (Python SDK)\n */\n const input = actionRequest.args || actionRequest.arguments;\n\n /**\n * Look up the original tool call ID using the name+args key\n * Fall back to action request ID or generate one if not found\n */\n const toolCallKey = `${toolName}:${JSON.stringify(input)}`;\n const toolCallId =\n emittedToolCallsByKey.get(toolCallKey) ||\n actionRequest.id ||\n `hitl-${toolName}-${Date.now()}`;\n\n /**\n * First emit tool-input-start then tool-input-available\n * so the UI knows what tool is being called with proper lifecycle\n */\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId,\n toolName,\n dynamic: true,\n });\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input,\n dynamic: true,\n });\n }\n\n /**\n * Then emit tool-approval-request to mark it as awaiting approval\n */\n controller.enqueue({\n type: 'tool-approval-request',\n approvalId: toolCallId,\n toolCallId,\n });\n }\n }\n }\n }\n\n break;\n }\n }\n}\n","import type { AIMessageChunk } from '@langchain/core/messages';\nimport type {\n UIMessage,\n UIMessageChunk,\n ChatTransport,\n ChatRequestOptions,\n} from 'ai';\nimport {\n RemoteGraph,\n type RemoteGraphParams,\n} from '@langchain/langgraph/remote';\nimport { toBaseMessages, toUIMessageStream } from './adapter';\n\n/**\n * Options for configuring a LangSmith deployment transport.\n * Extends RemoteGraphParams but makes graphId optional (defaults to 'agent').\n */\nexport type LangSmithDeploymentTransportOptions = Omit<\n RemoteGraphParams,\n 'graphId'\n> & {\n /**\n * The ID of the graph to connect to.\n * @default 'agent'\n */\n graphId?: string;\n};\n\n/**\n * A ChatTransport implementation for LangSmith/LangGraph deployments.\n *\n * This transport enables seamless integration between the AI SDK's useChat hook\n * and LangSmith deployed LangGraph agents.\n *\n * @example\n * ```ts\n * import { LangSmithDeploymentTransport } from '@ai-sdk/langchain';\n *\n * // Use with useChat\n * const { messages, input, handleSubmit } = useChat({\n * transport: new LangSmithDeploymentTransport({\n * url: 'https://your-deployment.us.langgraph.app',\n * apiKey: 'my-api-key',\n * }),\n * });\n * ```\n */\nexport class LangSmithDeploymentTransport<\n UI_MESSAGE extends UIMessage,\n> implements ChatTransport<UI_MESSAGE> {\n protected graph: RemoteGraph;\n\n constructor(options: LangSmithDeploymentTransportOptions) {\n this.graph = new RemoteGraph({\n ...options,\n graphId: options.graphId ?? 'agent',\n });\n }\n\n async sendMessages(\n options: {\n trigger: 'submit-message' | 'regenerate-message';\n chatId: string;\n messageId: string | undefined;\n messages: UI_MESSAGE[];\n abortSignal: AbortSignal | undefined;\n } & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk>> {\n const baseMessages = await toBaseMessages(options.messages);\n\n const stream = await this.graph.stream(\n { messages: baseMessages },\n { streamMode: ['values', 'messages'] },\n );\n\n return toUIMessageStream(\n stream as AsyncIterable<AIMessageChunk> | ReadableStream,\n );\n }\n\n async reconnectToStream(\n _options: {\n chatId: string;\n } & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n throw new Error('Method not implemented.');\n }\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAGK;AACP;AAAA,EACE;AAAA,OAIK;;;ACVP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AA2BA,SAAS,oBACd,OACgC;AAChC,SAAO,MAAM,WAAW,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AACxE;AAOO,SAAS,sBAAsB,OAAoC;AACxE,QAAM,WAAW,MAAM;AACrB,QAAI,MAAM,OAAO,SAAS,UAAU,MAAM,OAAO,SAAS,cAAc;AACtE,aAAO,MAAM,OAAO;AAAA,IACtB;AAEA,QAAI,MAAM,OAAO,SAAS,UAAU,MAAM,OAAO,SAAS,cAAc;AACtE,aAAO,KAAK,UAAU,MAAM,OAAO,KAAK;AAAA,IAC1C;AAEA,QAAI,MAAM,OAAO,SAAS,WAAW;AACnC,aAAO,MAAM,OAAO,MACjB,IAAI,iBAAe;AAClB,YAAI,YAAY,SAAS,QAAQ;AAC/B,iBAAO,YAAY;AAAA,QACrB;AACA,eAAO;AAAA,MACT,CAAC,EACA,KAAK,EAAE;AAAA,IACZ;AAEA,WAAO;AAAA,EACT,GAAG;AAEH,SAAO,IAAI,YAAY;AAAA,IACrB,cAAc,MAAM;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAOO,SAAS,wBAAwB,SAAsC;AAC5E,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,IAAI,UAAU,EAAE,QAAQ,CAAC;AAAA,EAClC;AAEA,QAAM,YAAsB,CAAC;AAC7B,QAAM,YAID,CAAC;AAEN,aAAW,QAAQ,SAAS;AAC1B,QAAI,KAAK,SAAS,QAAQ;AACxB,gBAAU,KAAK,KAAK,IAAI;AAAA,IAC1B,WAAW,KAAK,SAAS,aAAa;AACpC,gBAAU,KAAK;AAAA,QACb,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,IAAI,UAAU;AAAA,IACnB,SAAS,UAAU,KAAK,EAAE;AAAA,IAC1B,YAAY,UAAU,SAAS,IAAI,YAAY;AAAA,EACjD,CAAC;AACH;AAKA,SAAS,mBACP,WACA,SAAiB,QACT;AACR,QAAM,gBAAgB,UAAU,MAAM,GAAG,EAAE,CAAC,KAAK;AACjD,SAAO,GAAG,MAAM,IAAI,aAAa;AACnC;AA2BO,SAAS,mBAAmB,SAAoC;AArJvE;AAsJE,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,IAAI,aAAa,EAAE,QAAQ,CAAC;AAAA,EACrC;AAEA,QAAM,gBAA4C,CAAC;AAEnD,aAAW,QAAQ,SAAS;AAC1B,QAAI,KAAK,SAAS,QAAQ;AACxB,oBAAc,KAAK,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,CAAC;AAAA,IACtD,WAAW,KAAK,SAAS,SAAS;AAChC,YAAM,YAAY;AAUlB,UAAI,UAAU,iBAAiB,KAAK;AAClC,sBAAc,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,WAAW,EAAE,KAAK,UAAU,MAAM,SAAS,EAAE;AAAA,QAC/C,CAAC;AAAA,MACH,WAAW,OAAO,UAAU,UAAU,UAAU;AAO9C,YACE,UAAU,MAAM,WAAW,SAAS,KACpC,UAAU,MAAM,WAAW,UAAU,KACrC,UAAU,MAAM,WAAW,OAAO,GAClC;AAIA,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,WAAW,EAAE,KAAK,UAAU,MAAM;AAAA,UACpC,CAAC;AAAA,QACH,OAAO;AAIL,gBAAM,WAAW,UAAU,aAAa;AACxC,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,WAAW,EAAE,KAAK,QAAQ,QAAQ,WAAW,UAAU,KAAK,GAAG;AAAA,UACjE,CAAC;AAAA,QACH;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,QAIE,UAAU,iBAAiB,cAC3B,UAAU,iBAAiB;AAAA,QAC3B;AACA,cAAM,QACJ,UAAU,iBAAiB,cACvB,IAAI,WAAW,UAAU,KAAK,IAC9B,UAAU;AAIhB,cAAM,SAAS,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC;AACjD,cAAM,WAAW,UAAU,aAAa;AACxC,sBAAc,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,WAAW,EAAE,KAAK,QAAQ,QAAQ,WAAW,MAAM,GAAG;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF,WAAW,KAAK,SAAS,QAAQ;AAC/B,YAAM,cAAc;AAgBpB,YAAM,kBAA2D,MAAM;AACrE,cAAM,OAAO,YAAY;AACzB,YACE,OAAO,SAAS,YAChB,SAAS,QACT,EAAE,gBAAgB,QAClB,EAAE,gBAAgB,eAClB,EAAE,gBAAgB,gBAClB,UAAU,MACV;AACA,kBAAQ,KAAK,MAAM;AAAA,YACjB,KAAK;AACH,qBAAO,KAAK;AAAA,YACd,KAAK;AACH,qBAAO,KAAK;AAAA,YACd,KAAK;AACH,qBAAO,KAAK;AAAA,YACd;AACE,qBAAO;AAAA,UACX;AAAA,QACF;AACA,eAAO;AAAA,MACT,GAAG;AAEH,YAAM,WAAW;AAAA,QACf,MAAM;AAAA,QACN,MAAM;AAAA,QACN,WAAW,YAAY;AAAA,QACvB,UAAU,YAAY;AAAA,MACxB;AAKA,YAAM,WAAU,cAAS,cAAT,mBAAoB,WAAW;AAE/C,UAAI,SAAS;AAIX,YAAI,SAAS,gBAAgB,KAAK;AAChC,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,WAAW,EAAE,KAAK,SAAS,KAAK,SAAS,EAAE;AAAA,UAC7C,CAAC;AAAA,QACH,WAAW,OAAO,SAAS,SAAS,UAAU;AAI5C,cACE,SAAS,KAAK,WAAW,SAAS,KAClC,SAAS,KAAK,WAAW,UAAU,KACnC,SAAS,KAAK,WAAW,OAAO,GAChC;AACA,0BAAc,KAAK;AAAA,cACjB,MAAM;AAAA,cACN,WAAW,EAAE,KAAK,SAAS,KAAK;AAAA,YAClC,CAAC;AAAA,UACH,OAAO;AAIL,0BAAc,KAAK;AAAA,cACjB,MAAM;AAAA,cACN,WAAW;AAAA,gBACT,KAAK,QAAQ,SAAS,SAAS,WAAW,SAAS,IAAI;AAAA,cACzD;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF,WACE,SAAS,gBAAgB,cACzB,SAAS,gBAAgB,aACzB;AACA,gBAAM,QACJ,SAAS,gBAAgB,cACrB,IAAI,WAAW,SAAS,IAAI,IAC5B,SAAS;AACf,gBAAM,SAAS,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC;AACjD,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,WAAW,EAAE,KAAK,QAAQ,SAAS,SAAS,WAAW,MAAM,GAAG;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF,OAAO;AAEL,cAAM,WACJ,SAAS,YAAY,mBAAmB,SAAS,WAAW,MAAM;AAEpE,YAAI,SAAS,gBAAgB,KAAK;AAChC,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,KAAK,SAAS,KAAK,SAAS;AAAA,YAC5B,UAAU,SAAS;AAAA,YACnB;AAAA,UACF,CAAC;AAAA,QACH,WAAW,OAAO,SAAS,SAAS,UAAU;AAC5C,cACE,SAAS,KAAK,WAAW,SAAS,KAClC,SAAS,KAAK,WAAW,UAAU,GACnC;AACA,0BAAc,KAAK;AAAA,cACjB,MAAM;AAAA,cACN,KAAK,SAAS;AAAA,cACd,UAAU,SAAS;AAAA,cACnB;AAAA,YACF,CAAC;AAAA,UACH,WAAW,SAAS,KAAK,WAAW,OAAO,GAAG;AAC5C,kBAAM,UAAU,SAAS,KAAK,MAAM,4BAA4B;AAChE,gBAAI,SAAS;AACX,4BAAc,KAAK;AAAA,gBACjB,MAAM;AAAA,gBACN,MAAM,QAAQ,CAAC;AAAA,gBACf,UAAU,QAAQ,CAAC;AAAA,gBACnB;AAAA,cACF,CAAC;AAAA,YACH,OAAO;AACL,4BAAc,KAAK;AAAA,gBACjB,MAAM;AAAA,gBACN,KAAK,SAAS;AAAA,gBACd,UAAU,SAAS;AAAA,gBACnB;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF,OAAO;AACL,0BAAc,KAAK;AAAA,cACjB,MAAM;AAAA,cACN,MAAM,SAAS;AAAA,cACf,UAAU,SAAS;AAAA,cACnB;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF,WACE,SAAS,gBAAgB,cACzB,SAAS,gBAAgB,aACzB;AACA,gBAAM,QACJ,SAAS,gBAAgB,cACrB,IAAI,WAAW,SAAS,IAAI,IAC5B,SAAS;AACf,gBAAM,SAAS,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC;AACjD,wBAAc,KAAK;AAAA,YACjB,MAAM;AAAA,YACN,MAAM;AAAA,YACN,UAAU,SAAS;AAAA,YACnB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAKA,MAAI,cAAc,MAAM,WAAS,MAAM,SAAS,MAAM,GAAG;AACvD,WAAO,IAAI,aAAa;AAAA,MACtB,SAAS,cACN,IAAI,WAAU,MAAsC,IAAI,EACxD,KAAK,EAAE;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,aAAa,EAAE,SAAS,cAAc,CAAC;AACpD;AAOO,SAAS,iBAAiB,MAAuC;AACtE,SACE,QAAQ,QACR,OAAO,SAAS,YAChB,UAAU,QACT,KAA0B,SAAS;AAExC;AAQO,SAAS,kBACd,OACA,OAYA,YACM;AA1bR;AA8bE,MAAI,CAAC,MAAM,eAAe;AACxB,UAAM,gBAAgB,oBAAI,IAAY;AAAA,EACxC;AAEA,MAAI,CAAC,MAAM,kBAAkB;AAC3B,UAAM,mBAAmB,oBAAI,IAAY;AAAA,EAC3C;AAKA,MAAI,MAAM,IAAI;AACZ,UAAM,YAAY,MAAM;AAAA,EAC1B;AAKA,QAAM,WAAW;AACjB,QAAM,mBAAmB,SAAS;AAGlC,QAAM,eAAe,oBAAoB,gBAAgB;AAEzD,aAAW,eAAe,cAAc;AAItC,QAAI,YAAY,UAAU,CAAC,MAAM,cAAc,IAAI,YAAY,EAAE,GAAG;AAClE,YAAM,cAAc,IAAI,YAAY,EAAE;AAKtC,YAAM,YAAY,SAAS,YAAY,iBAAiB,KAAK;AAC7D,iBAAW,QAAQ;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA,KAAK,QAAQ,SAAS,WAAW,YAAY,MAAM;AAAA,MACrD,CAAC;AACD,YAAM,UAAU;AAAA,IAClB;AAAA,EACF;AAOA,QAAM,YACJ,kCAAkC,KAAK,KACvC,kCAAkC,KAAK;AACzC,MAAI,WAAW;AACb,QAAI,CAAC,MAAM,kBAAkB;AAE3B,YAAM,qBAAqB,MAAM;AACjC,iBAAW,QAAQ,EAAE,MAAM,mBAAmB,IAAI,MAAM,UAAU,CAAC;AACnE,YAAM,mBAAmB;AACzB,YAAM,UAAU;AAAA,IAClB;AACA,eAAW,QAAQ;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAI,WAAM,uBAAN,YAA4B,MAAM;AAAA,IACxC,CAAC;AAAA,EACH;AAKA,QAAM,OACJ,OAAO,MAAM,YAAY,WACrB,MAAM,UACN,MAAM,QAAQ,MAAM,OAAO,IACzB,MAAM,QACH;AAAA,IACC,CAAC,MACC,OAAO,MAAM,YACb,MAAM,QACN,UAAU,KACV,EAAE,SAAS;AAAA,EACf,EACC,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,EAAE,IACV;AAER,MAAI,MAAM;AAIR,QAAI,MAAM,oBAAoB,CAAC,MAAM,aAAa;AAChD,iBAAW,QAAQ;AAAA,QACjB,MAAM;AAAA,QACN,KAAI,WAAM,uBAAN,YAA4B,MAAM;AAAA,MACxC,CAAC;AACD,YAAM,mBAAmB;AAAA,IAC3B;AAEA,QAAI,CAAC,MAAM,aAAa;AAEtB,YAAM,gBAAgB,MAAM;AAC5B,iBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,MAAM,UAAU,CAAC;AAC9D,YAAM,cAAc;AACpB,YAAM,UAAU;AAAA,IAClB;AACA,eAAW,QAAQ;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAI,WAAM,kBAAN,YAAuB,MAAM;AAAA,IACnC,CAAC;AAAA,EACH;AAEA,QAAM,YAAY,kCAAkC,KAAK;AACzD,MAAI,UAAU,SAAS,GAAG;AACxB;AAAA,MACE;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,KAAuB;AAC1D,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAInD,SAAO,OAAQ,IAA+B,aAAa;AAC7D;AASO,SAAS,aAAa,KAAkC;AAC7D,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAEnD,QAAM,SAAS;AAKf,MAAI,OAAO,OAAO,OAAO,UAAU;AACjC,WAAO,OAAO;AAAA,EAChB;AAKA,MACE,OAAO,SAAS,iBAChB,OAAO,UACP,OAAO,OAAO,WAAW,UACzB;AACA,UAAM,SAAS,OAAO;AACtB,QAAI,OAAO,OAAO,OAAO,UAAU;AACjC,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAYO,SAAS,iBACd,KAC6D;AAI7D,MAAI,eAAe,WAAW,GAAG,EAAG,QAAO;AAI3C,MAAI,qBAAqB,GAAG,GAAG;AAC7B,UAAM,gBAAgB;AAKtB,QACE,UAAU,kBACT,cAAc,SAAS,QAAQ,cAAc,SAAS,mBACvD;AACA,aAAO;AAAA,IACT;AAIA,QACE,cAAc,SAAS,iBACvB,MAAM,QAAQ,cAAc,EAAE,MAC7B,cAAc,GAAG,SAAS,gBAAgB,KACzC,cAAc,GAAG,SAAS,WAAW,IACvC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,kBACd,KAC+D;AAC/D,MAAI,YAAY,WAAW,GAAG,EAAG,QAAO;AAIxC,MAAI,qBAAqB,GAAG,GAAG;AAC7B,UAAM,gBAAgB;AAItB,QAAI,UAAU,iBAAiB,cAAc,SAAS,OAAQ,QAAO;AAIrE,QACE,cAAc,SAAS,iBACvB,MAAM,QAAQ,cAAc,EAAE,KAC9B,cAAc,GAAG,SAAS,aAAa,GACvC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,eAAe,KAAsB;AAlsBrD;AAmsBE,MAAI,eAAe,WAAW,GAAG,GAAG;AAClC,YAAO,SAAI,SAAJ,YAAY;AAAA,EACrB;AAEA,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAEnD,QAAM,SAAS;AAGf,QAAM,aACJ,OAAO,SAAS,iBAChB,OAAO,UACP,OAAO,OAAO,WAAW,WACpB,OAAO,SACR;AAEN,MAAI,aAAa,YAAY;AAC3B,UAAM,UAAU,WAAW;AAI3B,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO;AAAA,IACT;AAIA,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,aAAO,QACJ;AAAA,QACC,CAAC,UACC,SAAS,QACT,OAAO,UAAU,YACjB,MAAM,SAAS,UACf,OAAO,MAAM,SAAS;AAAA,MAC1B,EACC,IAAI,WAAS,MAAM,IAAI,EACvB,KAAK,EAAE;AAAA,IACZ;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAQO,SAAS,wBACd,KAC8B;AAC9B,SACE,OAAO,QACP,OAAO,QAAQ,YACf,UAAU,OACT,IAAyB,SAAS,eACnC,eAAe,OACf,OAAQ,IAA+B,cAAc;AAEzD;AAQO,SAAS,uBACd,KAC6B;AAC7B,SACE,OAAO,QACP,OAAO,QAAQ,YACf,UAAU,OACT,IAAyB,SAAS,cACnC,cAAc,OACd,OAAQ,IAA8B,aAAa;AAEvD;AAKA,SAAS,sBAAsB,KAA0C;AACvE,SACE,OAAO,QACP,OAAO,QAAQ,YACf,UAAU,OACT,IAAyB,SAAS,eACnC,aAAa,OACb,MAAM,QAAS,IAA6B,OAAO;AAEvD;AAUO,SAAS,mBAAmB,KAAkC;AA3yBrE;AA4yBE,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAGnD,QAAM,SAAS;AACf,QAAM,SACJ,OAAO,UAAU,OAAO,OAAO,WAAW,WACrC,OAAO,SACR;AAGN,QAAM,mBACJ,OACA;AACF,OAAI,0DAAkB,cAAlB,mBAA6B,IAAI;AACnC,WAAO,iBAAiB,UAAU;AAAA,EACpC;AAGA,QAAM,mBACJ,OACA;AACF,MAAI,oBAAoB,MAAM,QAAQ,iBAAiB,MAAM,GAAG;AAC9D,eAAW,QAAQ,iBAAiB,QAAQ;AAC1C,UAAI,sBAAsB,IAAI,GAAG;AAC/B,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAgBO,SAAS,kCACd,KACoB;AACpB,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAGnD,QAAM,SAAS;AACf,QAAM,SACJ,OAAO,UAAU,OAAO,OAAO,WAAW,WACrC,OAAO,SACR;AAGN,QAAM,gBAAiB,OAAyC;AAChE,MAAI,MAAM,QAAQ,aAAa,GAAG;AAChC,UAAM,iBAA2B,CAAC;AAClC,eAAW,SAAS,eAAe;AACjC,UAAI,wBAAwB,KAAK,GAAG;AAClC,uBAAe,KAAK,MAAM,SAAS;AAAA,MACrC,WAAW,uBAAuB,KAAK,GAAG;AACxC,uBAAe,KAAK,MAAM,QAAQ;AAAA,MACpC;AAAA,IACF;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,aAAO,eAAe,KAAK,EAAE;AAAA,IAC/B;AAAA,EACF;AAKA,QAAM,mBACJ,OACA;AACF,OACE,qDAAkB,cAClB,MAAM,QAAQ,iBAAiB,UAAU,OAAO,GAChD;AACA,UAAM,iBAA2B,CAAC;AAClC,eAAW,eAAe,iBAAiB,UAAU,SAAS;AAC5D,UACE,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,UAAU,eACV,OAAQ,YAAkC,SAAS,UACnD;AACA,uBAAe,KAAM,YAAiC,IAAI;AAAA,MAC5D;AAAA,IACF;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,aAAO,eAAe,KAAK,EAAE;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AASO,SAAS,kCACd,KACoB;AACpB,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO;AAGnD,QAAM,SAAS;AACf,QAAM,SACJ,OAAO,UAAU,OAAO,OAAO,WAAW,WACrC,OAAO,SACR;AAGN,QAAM,mBACJ,OACA;AACF,MAAI,oBAAoB,MAAM,QAAQ,iBAAiB,MAAM,GAAG;AAC9D,UAAM,iBAA2B,CAAC;AAClC,eAAW,QAAQ,iBAAiB,QAAQ;AAC1C,UAAI,sBAAsB,IAAI,GAAG;AAE/B,mBAAW,eAAe,KAAK,SAAS;AACtC,cAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM;AAC3D,kBAAM,OAAQ,YAAkC;AAChD,gBAAI,OAAO,SAAS,YAAY,MAAM;AACpC,6BAAe,KAAK,IAAI;AAAA,YAC1B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,aAAO,eAAe,KAAK,EAAE;AAAA,IAC/B;AAAA,EACF;AAGA,QAAM,mBACJ,OACA;AACF,OACE,qDAAkB,cAClB,MAAM,QAAQ,iBAAiB,UAAU,OAAO,GAChD;AACA,UAAM,iBAA2B,CAAC;AAClC,eAAW,eAAe,iBAAiB,UAAU,SAAS;AAC5D,UACE,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,UAAU,eACV,OAAQ,YAAkC,SAAS,UACnD;AACA,uBAAe,KAAM,YAAiC,IAAI;AAAA,MAC5D;AAAA,IACF;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,aAAO,eAAe,KAAK,EAAE;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,uBACd,KAC8B;AAC9B,SACE,OAAO,QACP,OAAO,QAAQ,YACf,UAAU,OACT,IAA0B,SAAS;AAExC;AAUO,SAAS,kCACd,KACsB;AACtB,MAAI,OAAO,QAAQ,OAAO,QAAQ,SAAU,QAAO,CAAC;AAEpD,QAAM,SAAS;AACf,QAAM,SACJ,OAAO,UAAU,OAAO,OAAO,WAAW,WACrC,OAAO,SACR;AAIN,QAAM,gBAAiB,OAAuC;AAC9D,QAAM,UAAW,OAAiC;AAClD,QAAM,eAA0B,MAAM,QAAQ,aAAa,IACvD,gBACA,MAAM,QAAQ,OAAO,IACnB,UACA,CAAC;AAEP,QAAM,YAAkC,CAAC;AAEzC,aAAW,SAAS,cAAc;AAChC,QAAI,SAAS,QAAQ,OAAO,UAAU,SAAU;AAEhD,UAAM,cAAe,MAAoC;AACzD,QAAI,CAAC,MAAM,QAAQ,WAAW,EAAG;AAEjC,eAAW,cAAc,aAAa;AACpC,UAAI,CAAC,uBAAuB,UAAU,EAAG;AAEzC,gBAAU,KAAK;AAAA,QACb,KAAK,WAAW;AAAA,QAChB,OAAO,WAAW;AAAA,QAClB,QAAQ,WAAW;AAAA,QACnB,WAAW,WAAW;AAAA,QACtB,YAAY,WAAW;AAAA,QACvB,UAAU,WAAW;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,4BACP,UAC8B;AAC9B,QAAM,YAAuC,CAAC;AAE9C,MAAI,OAAO,SAAS,cAAc,UAAU;AAC1C,cAAU,YAAY,SAAS;AAAA,EACjC;AACA,MAAI,OAAO,SAAS,eAAe,UAAU;AAC3C,cAAU,aAAa,SAAS;AAAA,EAClC;AACA,MAAI,OAAO,SAAS,aAAa,UAAU;AACzC,cAAU,WAAW,SAAS;AAAA,EAChC;AACA,MAAI,OAAO,SAAS,WAAW,UAAU;AACvC,cAAU,SAAS,SAAS;AAAA,EAC9B;AAEA,MAAI,OAAO,KAAK,SAAS,EAAE,WAAW,GAAG;AACvC,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,UAAU;AACrB;AAgBO,SAAS,iBACd,WACA,WACA,kBACA,YACM;AAtkCR;AAukCE,aAAW,YAAY,WAAW;AAChC,QAAI,SAAS,KAAK;AAChB,UAAI,iBAAiB,IAAI,SAAS,GAAG,EAAG;AACxC,uBAAiB,IAAI,SAAS,GAAG;AAEjC,YAAMA,oBAAmB,4BAA4B,QAAQ;AAC7D,iBAAW,QAAQ;AAAA,QACjB,MAAM;AAAA,QACN,UAAU,SAAS;AAAA,QACnB,KAAK,SAAS;AAAA,QACd,GAAI,SAAS,QAAQ,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,QAClD,GAAIA,oBAAmB,EAAE,kBAAAA,kBAAiB,IAAI,CAAC;AAAA,MACjD,CAAC;AACD;AAAA,IACF;AAIA,UAAM,SAAQ,cAAS,UAAT,YAAkB,SAAS;AACzC,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAW,GAAG,SAAS,IAAI,KAAK,KAAI,cAAS,cAAT,YAAsB,EAAE,KAAI,cAAS,eAAT,YAAuB,EAAE,KAAI,cAAS,aAAT,YAAqB,EAAE;AAC1H,QAAI,iBAAiB,IAAI,QAAQ,EAAG;AACpC,qBAAiB,IAAI,QAAQ;AAE7B,UAAM,mBAAmB,4BAA4B,QAAQ;AAC7D,eAAW,QAAQ;AAAA,MACjB,MAAM;AAAA,MACN;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,IACjD,CAAC;AAAA,EACH;AACF;AAQO,SAAS,wBACd,KAC8B;AAC9B,SACE,OAAO,QACP,OAAO,QAAQ,YACf,UAAU,OACT,IAAyB,SAAS;AAEvC;AAQO,SAAS,oBACd,kBACyB;AACzB,MAAI,CAAC,iBAAkB,QAAO,CAAC;AAE/B,QAAM,cAAc,iBAAiB;AACrC,MAAI,CAAC,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC;AAEzC,SAAO,YAAY,OAAO,uBAAuB;AACnD;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,MAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,MAAI;AACF,UAAM,aAAa,KAAK,UAAU,KAAK;AACvC,WAAO,kCAAc,OAAO,KAAK;AAAA,EACnC,SAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAMA,SAAS,uBACP,aACA,OACsB;AACtB,MAAI,OAAO,YAAY,IAAI,KAAK;AAChC,MAAI,CAAC,MAAM;AACT,WAAO,CAAC;AACR,gBAAY,IAAI,OAAO,IAAI;AAAA,EAC7B;AACA,SAAO;AACT;AAMA,SAAS,+BACP,qBACA,OAC2C;AAC3C,MAAI,eAAe,oBAAoB,IAAI,KAAK;AAChD,MAAI,CAAC,cAAc;AACjB,mBAAe,oBAAI,IAAI;AACvB,wBAAoB,IAAI,OAAO,YAAY;AAAA,EAC7C;AACA,SAAO;AACT;AASO,SAAS,sBACd,OACA,OACA,YACM;AApsCR;AAqsCE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,CAAC,MAAM,IAAI,IAAI,oBAAoB,KAAK;AAE9C,UAAQ,MAAM;AAAA,IACZ,KAAK,UAAU;AAYb,UAAI,iBAAiB;AACrB,UAAI;AAEJ,UAAI,QAAQ,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AACpE,cAAM,UAAU;AAChB,YAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,MAAM;AACpD,2BAAiB,QAAQ;AAAA,QAC3B;AACA,YAAI,OAAO,QAAQ,OAAO,YAAY,QAAQ,IAAI;AAChD,mBAAS,QAAQ;AAAA,QACnB;AAAA,MACF;AAEA,iBAAW,QAAQ;AAAA,QACjB,MAAM,QAAQ,cAAc;AAAA,QAC5B,IAAI;AAAA,QACJ,WAAW,UAAU;AAAA,QACrB;AAAA,MACF,CAAC;AACD;AAAA,IACF;AAAA,IAEA,KAAK,YAAY;AACf,YAAM,CAAC,QAAQ,QAAQ,IAAI;AAK3B,YAAM,MAAM;AACZ,YAAM,QAAQ,aAAa,GAAG;AAE9B,UAAI,CAAC,MAAO;AAQZ,YAAM,gBACJ,QAAO,qCAAU,oBAAmB,WAChC,SAAS,iBACT;AACN,UAAI,kBAAkB,QAAQ,kBAAkB,MAAM,aAAa;AACjE,YAAI,MAAM,gBAAgB,MAAM;AAC9B,qBAAW,CAAC,IAAI,IAAI,KAAK,aAAa;AACpC,gBAAI,KAAK,MAAM;AACb,yBAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAAA,YAC7C;AACA,gBAAI,KAAK,WAAW;AAClB,yBAAW,QAAQ,EAAE,MAAM,iBAAiB,GAAG,CAAC;AAAA,YAClD;AACA,wBAAY,OAAO,EAAE;AACrB,0BAAc,OAAO,EAAE;AACvB,gCAAoB,OAAO,EAAE;AAAA,UAC/B;AACA,qBAAW,QAAQ,EAAE,MAAM,cAAc,CAAC;AAAA,QAC5C;AACA,mBAAW,QAAQ,EAAE,MAAM,aAAa,CAAC;AACzC,cAAM,cAAc;AAAA,MACtB;AAMA,UAAI,eAAe,WAAW,GAAG,GAAG;AAClC,cAAM,kBAAkB,cAAc,IAAI,KAAK;AAC/C,YAAI,iBAAiB;AACnB,wBAAc;AAAA,YACZ;AAAA,YACA,gBAAgB,OAAO,GAAG;AAAA,UAC5B;AAAA,QACF,OAAO;AACL,wBAAc,IAAI,OAAO,GAAG;AAAA,QAC9B;AAAA,MACF;AAEA,UAAI,iBAAiB,GAAG,GAAG;AACzB,cAAM,cAAc,cAAc,IAAI,KAAK;AAM3C,cAAM,SAAS;AACf,cAAM,aACJ,OAAO,SAAS,iBAChB,OAAO,UACP,OAAO,OAAO,WAAW,WACpB,OAAO,SACR;AACN,cAAM,mBAAmB,WAAW;AAGpC,cAAM,eAAe,oBAAoB,gBAAgB;AAEzD,mBAAW,eAAe,cAAc;AAItC,cAAI,YAAY,UAAU,CAAC,cAAc,IAAI,YAAY,EAAE,GAAG;AAC5D,0BAAc,IAAI,YAAY,EAAE;AAKhC,kBAAM,YAAY,SAAS,YAAY,iBAAiB,KAAK;AAC7D,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN;AAAA,cACA,KAAK,QAAQ,SAAS,WAAW,YAAY,MAAM;AAAA,YACrD,CAAC;AAAA,UACH;AAAA,QACF;AAaA,cAAM,iBAAiB,WAAW;AAGlC,YAAI,iDAAgB,QAAQ;AAC1B,qBAAW,iBAAiB,gBAAgB;AAC1C,kBAAM,iBAAgB,mBAAc,UAAd,YAAuB;AAK7C,gBAAI,cAAc,IAAI;AACpB,6CAA+B,qBAAqB,KAAK,EAAE;AAAA,gBACzD;AAAA,gBACA;AAAA,kBACE,IAAI,cAAc;AAAA,kBAClB,MACE,cAAc,UACd,sDAAa,qBAAb,mBAAgC,mBAAhC,mBAAgD,SAChD;AAAA,gBACJ;AAAA,cACF;AAAA,YACF;AAKA,kBAAM,sBAAqB,yBACxB,IAAI,KAAK,MADe,mBAEvB,IAAI;AACR,kBAAM,aACJ,cAAc,OACd,yDAAoB,SACpB,sDAAa,qBAAb,mBAAgC,mBAAhC,mBAAgD;AAKlD,gBAAI,CAAC,YAAY;AACf;AAAA,YACF;AAEA,kBAAM,WACJ,cAAc,SACd,yDAAoB,WACpB,sDAAa,qBAAb,mBAAgC,mBAAhC,mBAAgD,SAChD;AAOF,kBAAM,OAAO,YAAY,IAAI,KAAK;AAClC,gBAAI,GAAC,kCAAM,SAAN,mBAAY,IAAI,cAAa;AAChC,oBAAM,cAAc,uBAAuB,aAAa,KAAK;AAC7D,gCAAY,SAAZ,wBAAY,OAAS,oBAAI,IAAI;AAC7B,0BAAY,KAAK,IAAI,UAAU;AAE/B,kBAAI,CAAC,iBAAiB,IAAI,UAAU,GAAG;AACrC,iCAAiB,IAAI,UAAU;AAC/B,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAKA,gBAAI,cAAc,MAAM;AACtB,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN;AAAA,gBACA,gBAAgB,cAAc;AAAA,cAChC,CAAC;AAAA,YACH;AAAA,UACF;AAEA;AAAA,QACF;AAaA,cAAM,mBAAmB,mBAAmB,GAAG;AAC/C,YAAI,kBAAkB;AACpB,cAAI,CAAC,oBAAoB,IAAI,KAAK,GAAG;AACnC,gCAAoB,IAAI,OAAO,gBAAgB;AAAA,UACjD;AAGA,8BAAoB,IAAI,gBAAgB;AAAA,QAC1C;AAEA,cAAM,YAAY,kCAAkC,GAAG;AACvD,YAAI,WAAW;AAEb,gBAAM,eACJ,+BAAoB,IAAI,KAAK,MAA7B,YAAkC,qBAAlC,YAAsD;AAExD,gBAAM,OAAO,YAAY,IAAI,KAAK;AAClC,cAAI,EAAC,6BAAM,YAAW;AACpB,uBAAW,QAAQ,EAAE,MAAM,mBAAmB,IAAI,MAAM,CAAC;AACzD,mCAAuB,aAAa,KAAK,EAAE,YAAY;AAAA,UACzD;AAGA,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,IAAI;AAAA,UACN,CAAC;AAED,8BAAoB,IAAI,WAAW;AAAA,QACrC;AAKA,cAAM,OAAO,eAAe,GAAG;AAC/B,YAAI,MAAM;AACR,gBAAM,OAAO,YAAY,IAAI,KAAK;AAClC,cAAI,EAAC,6BAAM,OAAM;AACf,uBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,MAAM,CAAC;AACpD,mCAAuB,aAAa,KAAK,EAAE,OAAO;AAAA,UACpD;AAEA,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,IAAI;AAAA,UACN,CAAC;AAAA,QACH;AAEA,cAAM,YAAY,kCAAkC,GAAG;AACvD,YAAI,UAAU,SAAS,GAAG;AACxB;AAAA,YACE;AAAA,YACA;AAAA,YACA,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,kBAAkB,GAAG,GAAG;AAEjC,cAAM,SAAS;AACf,cAAM,aACJ,OAAO,SAAS,iBAChB,OAAO,UACP,OAAO,OAAO,WAAW,WACpB,OAAO,SACR;AAEN,cAAM,aAAa,WAAW;AAC9B,cAAM,SAAS,WAAW;AAE1B,YAAI,YAAY;AACd,cAAI,WAAW,SAAS;AAEtB,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN;AAAA,cACA,WACE,OAAO,WAAW,YAAY,WAC1B,WAAW,UACX;AAAA,YACR,CAAC;AAAA,UACH,OAAO;AAEL,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN;AAAA,cACA,QAAQ,WAAW;AAAA,YACrB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA;AAAA,IACF;AAAA,IAEA,KAAK,SAAS;AACZ,UAAI,QAAQ,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACnE;AAAA,MACF;AAEA,YAAM,UAAU;AAShB,YAAM,aACJ,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa;AAChE,YAAM,WACJ,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAEpD,UAAI,CAAC,WAAY;AAEjB,YAAM,2BAA2B,MAAM;AACrC,YAAI,CAAC,iBAAiB,IAAI,UAAU,GAAG;AACrC,2BAAiB,IAAI,UAAU;AAC/B,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,YAAI,CAAC,kBAAkB,IAAI,UAAU,GAAG;AACtC,4BAAkB,IAAI,UAAU;AAChC,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,OAAO,QAAQ;AAAA,YACf,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAAA,MACF;AAEA,cAAQ,QAAQ,OAAO;AAAA,QACrB,KAAK,iBAAiB;AACpB,gBAAM,cAAc,GAAG,QAAQ,IAAI,KAAK,UAAU,QAAQ,KAAK,CAAC;AAChE,gCAAsB,IAAI,aAAa,UAAU;AAEjD,mCAAyB;AACzB;AAAA,QACF;AAAA,QAEA,KAAK,iBAAiB;AACpB,mCAAyB;AACzB,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN;AAAA,YACA,QAAQ,QAAQ;AAAA,YAChB,aAAa;AAAA,UACf,CAAC;AACD;AAAA,QACF;AAAA,QAEA,KAAK,eAAe;AAClB,mCAAyB;AACzB,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN;AAAA,YACA,QAAQ,QAAQ;AAAA,UAClB,CAAC;AACD;AAAA,QACF;AAAA,QAEA,KAAK,iBAAiB;AACpB,mCAAyB;AACzB,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN;AAAA,YACA,WAAW,gBAAgB,QAAQ,KAAK;AAAA,UAC1C,CAAC;AACD;AAAA,QACF;AAAA,MACF;AAEA;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AAIb,iBAAW,CAAC,IAAI,IAAI,KAAK,aAAa;AACpC,YAAI,KAAK,KAAM,YAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAC1D,YAAI,KAAK,MAAM;AACb,qBAAW,cAAc,KAAK,MAAM;AAClC,kBAAM,YAAY,cAAc,IAAI,EAAE;AACtC,kBAAM,YAAW,4CAAW,eAAX,mBAAuB;AAAA,cACtC,UAAQ,KAAK,OAAO;AAAA;AAGtB,gBAAI,UAAU;AACZ,+BAAiB,IAAI,UAAU;AAE/B,oBAAM,cAAc,GAAG,SAAS,IAAI,IAAI,KAAK,UAAU,SAAS,IAAI,CAAC;AACrE,oCAAsB,IAAI,aAAa,UAAU;AACjD,kBAAI,CAAC,kBAAkB,IAAI,UAAU,GAAG;AACtC,kCAAkB,IAAI,UAAU;AAChC,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN;AAAA,kBACA,UAAU,SAAS;AAAA,kBACnB,OAAO,SAAS;AAAA,kBAChB,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,YAAI,KAAK,WAAW;AAClB,qBAAW,QAAQ,EAAE,MAAM,iBAAiB,GAAG,CAAC;AAAA,QAClD;AAEA,oBAAY,OAAO,EAAE;AACrB,sBAAc,OAAO,EAAE;AACvB,4BAAoB,OAAO,EAAE;AAAA,MAC/B;AAMA,UAAI,QAAQ,QAAQ,OAAO,SAAS,YAAY,cAAc,MAAM;AAClE,cAAM,WAAY,KAAkC;AACpD,YAAI,MAAM,QAAQ,QAAQ,GAAG;AAK3B,gBAAM,uBAAuB,oBAAI,IAAY;AAC7C,qBAAW,OAAO,UAAU;AAC1B,gBAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AAErC,gBAAI,kBAAkB,GAAG,GAAG;AAE1B,oBAAM,SAAS;AACf,oBAAM,aACJ,OAAO,SAAS,iBAChB,OAAO,UACP,OAAO,OAAO,WAAW,WACpB,OAAO,SACR;AAEN,oBAAM,aAAa,WAAW;AAC9B,kBAAI,YAAY;AACd,qCAAqB,IAAI,UAAU;AAAA,cACrC;AAAA,YACF;AAAA,UACF;AAMA,qBAAW,OAAO,UAAU;AAC1B,gBAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AAGrC,kBAAM,QAAQ,aAAa,GAAG;AAC9B,gBAAI,CAAC,MAAO;AAKZ,gBAAI;AAKJ,gBAAI,eAAe,WAAW,GAAG,KAAK,UAAU,WAAW,GAAG,GAAG;AAC/D,0BAAY,IAAI;AAAA,YAClB,WAAW,qBAAqB,GAAG,GAAG;AAIpC,oBAAM,gBAAgB;AAKtB,oBAAM,qBACJ,cAAc,SAAS,iBACvB,MAAM,QAAQ,cAAc,EAAE,MAC5B,cAAc,GAAgB,SAAS,gBAAgB,KACtD,cAAc,GAAgB,SAAS,WAAW;AACvD,oBAAM,aAAa,qBACd,cAAc,SACf;AAEJ,kBACE,cAAc,SAAS,QACvB,cAAc,SAAS,oBACvB,oBACA;AAIA,oBAAI,MAAM,QAAQ,yCAAY,UAAU,GAAG;AACzC,8BAAY,WAAW;AAAA,gBACzB;AAAA;AAAA;AAAA;AAAA,mBAIE,yCAAY,sBACZ,OAAO,WAAW,sBAAsB;AAAA,kBACxC;AACA,wBAAM,mBACJ,WAAW;AACb,sBAAI,MAAM,QAAQ,iBAAiB,UAAU,GAAG;AAI9C,gCACE,iBAAiB,WAIjB,IAAI,CAAC,IAAI,QAAQ;AACjB,4BAAM,eAAe,GAAG;AACxB,0BAAI;AACJ,0BAAI;AACF,gCAAO,6CAAc,aACjB,KAAK,MAAM,aAAa,SAAS,IACjC,CAAC;AAAA,sBACP,SAAQ;AACN,+BAAO,CAAC;AAAA,sBACV;AACA,6BAAO;AAAA,wBACL,IAAI,GAAG,MAAM,QAAQ,GAAG;AAAA,wBACxB,OAAM,6CAAc,SAAQ;AAAA,wBAC5B;AAAA,sBACF;AAAA,oBACF,CAAC;AAAA,kBACH;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAEA,gBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,yBAAW,YAAY,WAAW;AAOhC,oBACE,SAAS,MACT,CAAC,iBAAiB,IAAI,SAAS,EAAE,KACjC,CAAC,qBAAqB,IAAI,SAAS,EAAE,GACrC;AACA,mCAAiB,IAAI,SAAS,EAAE;AAEhC,wBAAM,cAAc,GAAG,SAAS,IAAI,IAAI,KAAK,UAAU,SAAS,IAAI,CAAC;AACrE,wCAAsB,IAAI,aAAa,SAAS,EAAE;AAMlD,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,YAAY,SAAS;AAAA,oBACrB,UAAU,SAAS;AAAA,oBACnB,SAAS;AAAA,kBACX,CAAC;AACD,oCAAkB,IAAI,SAAS,EAAE;AACjC,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,YAAY,SAAS;AAAA,oBACrB,UAAU,SAAS;AAAA,oBACnB,OAAO,SAAS;AAAA,oBAChB,SAAS;AAAA,kBACX,CAAC;AAAA,gBACH,WAAW,SAAS,MAAM,iBAAiB,IAAI,SAAS,EAAE,GAAG;AAG3D,wBAAM,cAAc,GAAG,SAAS,IAAI,IAAI,KAAK,UAAU,SAAS,IAAI,CAAC;AACrE,wCAAsB,IAAI,aAAa,SAAS,EAAE;AAAA,gBACpD;AAAA,cACF;AAAA,YACF;AAYA,kBAAM,cAAc,mBAAmB,GAAG;AAC1C,kBAAM,yBAAyB,YAAY,IAAI,KAAK;AACpD,kBAAM,eAAe,aAAa,UAAU,SAAS;AASrD,kBAAM,sBACJ,eACA,CAAC,oBAAoB,IAAI,WAAW,MACnC,0BAA0B,CAAC;AAE9B,gBAAI,qBAAqB;AAKvB,oBAAM,YAAY,kCAAkC,GAAG;AAEvD,kBAAI,WAAW;AACb,2BAAW,QAAQ,EAAE,MAAM,mBAAmB,IAAI,MAAM,CAAC;AACzD,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP,IAAI;AAAA,gBACN,CAAC;AACD,2BAAW,QAAQ,EAAE,MAAM,iBAAiB,IAAI,MAAM,CAAC;AACvD,oCAAoB,IAAI,WAAW;AAAA,cACrC;AAAA,YACF;AAEA,kBAAM,kBAAkB,kCAAkC,GAAG;AAC7D,gBAAI,gBAAgB,SAAS,GAAG;AAC9B;AAAA,gBACE;AAAA,gBACA;AAAA,gBACA,MAAM;AAAA,gBACN;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAOA,UAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU;AAC5C,cAAM,YAAa,KAAiC;AACpD,YAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,GAAG;AACpD,qBAAW,iBAAiB,WAAW;AACrC,kBAAM,iBAAkB,+CACpB;AAEJ,gBAAI,CAAC,eAAgB;AAKrB,kBAAM,iBAAkB,eAAe,kBACrC,eAAe;AASjB,gBAAI,CAAC,MAAM,QAAQ,cAAc,EAAG;AAEpC,uBAAW,iBAAiB,gBAAgB;AAC1C,oBAAM,WAAW,cAAc;AAI/B,oBAAM,QAAQ,cAAc,QAAQ,cAAc;AAMlD,oBAAM,cAAc,GAAG,QAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AACxD,oBAAM,aACJ,sBAAsB,IAAI,WAAW,KACrC,cAAc,MACd,QAAQ,QAAQ,IAAI,KAAK,IAAI,CAAC;AAMhC,kBAAI,CAAC,iBAAiB,IAAI,UAAU,GAAG;AACrC,iCAAiB,IAAI,UAAU;AAC/B,sCAAsB,IAAI,aAAa,UAAU;AACjD,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,gBACX,CAAC;AACD,kCAAkB,IAAI,UAAU;AAChC,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAKA,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN,YAAY;AAAA,gBACZ;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA;AAAA,IACF;AAAA,EACF;AACF;;;ADt6DA,eAAsB,eACpB,UACwB;AACxB,QAAM,gBAAgB,MAAM,uBAAuB,QAAQ;AAC3D,SAAO,qBAAqB,aAAa;AAC3C;AAQO,SAAS,qBACd,eACe;AACf,QAAM,SAAwB,CAAC;AAE/B,aAAW,WAAW,eAAe;AACnC,YAAQ,QAAQ,MAAM;AAAA,MACpB,KAAK,QAAQ;AAEX,mBAAW,QAAQ,QAAQ,SAAS;AAClC,cAAI,iBAAiB,IAAI,GAAG;AAC1B,mBAAO,KAAK,sBAAsB,IAAI,CAAC;AAAA,UACzC;AAAA,QACF;AACA;AAAA,MACF;AAAA,MAEA,KAAK,aAAa;AAChB,eAAO,KAAK,wBAAwB,QAAQ,OAAO,CAAC;AACpD;AAAA,MACF;AAAA,MAEA,KAAK,UAAU;AACb,eAAO,KAAK,IAAI,cAAc,EAAE,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAC3D;AAAA,MACF;AAAA,MAEA,KAAK,QAAQ;AACX,eAAO,KAAK,mBAAmB,QAAQ,OAAO,CAAC;AAC/C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,oBACP,OAC2D;AAC3D,MAAI,SAAS,QAAQ,OAAO,UAAU,SAAU,QAAO;AACvD,QAAM,MAAM;AAEZ,MAAI,EAAE,WAAW,QAAQ,OAAO,IAAI,UAAU,SAAU,QAAO;AAE/D,MAAI,EAAE,UAAU,KAAM,QAAO;AAE7B,SAAO,IAAI,SAAS,QAAQ,OAAO,IAAI,SAAS;AAClD;AASA,SAAS,yBACP,OAMA,OASA,YACM;AA3IR;AA+IE,MAAI,MAAM,UAAU,CAAC,MAAM,SAAS;AAClC,UAAM,YAAY,MAAM;AAAA,EAC1B;AAKA,MAAI,CAAC,MAAM,KAAM;AAEjB,UAAQ,MAAM,OAAO;AAAA,IACnB,KAAK,uBAAuB;AAM1B,UAAI,MAAM,kBAAkB;AAC1B,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,IACE,MAAM,sBAAsB,OACxB,MAAM,qBACN,MAAM;AAAA,QACd,CAAC;AACD,cAAM,mBAAmB;AACzB,cAAM,qBAAqB;AAAA,MAC7B;AAQA,UAAI,MAAM,aAAa;AACrB,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,IACE,MAAM,iBAAiB,OAAO,MAAM,gBAAgB,MAAM;AAAA,QAC9D,CAAC;AACD,cAAM,cAAc;AACpB,cAAM,gBAAgB;AAAA,MACxB;AAMA,YAAM,QAAQ,MAAM,UAAW,MAAM,KAAK;AAC1C,UAAI,OAAO;AACT,cAAM,YAAY;AAAA,MACpB;AACA;AAAA,IACF;AAAA,IAEA,KAAK,wBAAwB;AAI3B,YAAM,QAAQ,MAAM,KAAK;AACzB,UAAI,SAAS,OAAO,UAAU,UAAU;AAItC,cAAM,UAAW,MAA0B;AAC3C,YAAI,SAAS;AACX,gBAAM,YAAY;AAAA,QACpB;AAKA,cAAM,YAAY,kCAAkC,KAAK;AACzD,YAAI,WAAW;AACb,cAAI,CAAC,MAAM,kBAAkB;AAE3B,kBAAM,qBAAqB,MAAM;AACjC,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,IAAI,MAAM;AAAA,YACZ,CAAC;AACD,kBAAM,mBAAmB;AACzB,kBAAM,UAAU;AAAA,UAClB;AACA,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,KAAI,WAAM,uBAAN,YAA4B,MAAM;AAAA,UACxC,CAAC;AAAA,QACH;AAKA,cAAM,UAAW,MAAgC;AACjD,cAAM,OACJ,OAAO,YAAY,WACf,UACA,MAAM,QAAQ,OAAO,IACnB,QACG;AAAA,UACC,CAAC,MACC,OAAO,MAAM,YACb,MAAM,QACN,UAAU,KACV,EAAE,SAAS;AAAA,QACf,EACC,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,EAAE,IACV;AAER,YAAI,MAAM;AAIR,cAAI,MAAM,oBAAoB,CAAC,MAAM,aAAa;AAChD,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,KAAI,WAAM,uBAAN,YAA4B,MAAM;AAAA,YACxC,CAAC;AACD,kBAAM,mBAAmB;AAAA,UAC3B;AAEA,cAAI,CAAC,MAAM,aAAa;AAEtB,kBAAM,gBAAgB,MAAM;AAC5B,uBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,MAAM,UAAU,CAAC;AAC9D,kBAAM,cAAc;AACpB,kBAAM,UAAU;AAAA,UAClB;AACA,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,KAAI,WAAM,kBAAN,YAAuB,MAAM;AAAA,UACnC,CAAC;AAAA,QACH;AAEA,cAAM,YAAY,kCAAkC,KAAK;AACzD,YAAI,UAAU,SAAS,GAAG;AACxB;AAAA,YACE;AAAA,YACA,MAAM;AAAA,YACN,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAAA,IAEA,KAAK,iBAAiB;AAKpB,YAAM,QAAQ,MAAM,UAAW,MAAM,KAAK;AAC1C,YAAM,OAAO,MAAM,QAAS,MAAM,KAAK;AAEvC,UAAI,SAAS,MAAM;AACjB,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,YAAY;AAAA,UACZ,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAAA,IAEA,KAAK,eAAe;AAKlB,YAAM,QAAQ,MAAM,UAAW,MAAM,KAAK;AAC1C,YAAM,SAAS,MAAM,KAAK;AAE1B,UAAI,OAAO;AACT,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAAA,EACF;AACF;AAKA,SAAS,aAAa,OAAyB;AAC7C,MAAI,iBAAiB,OAAO;AAC1B,WACE,MAAM,SAAS,gBACd,iBAAiB,gBAAgB,MAAM,SAAS;AAAA,EAErD;AACA,SAAO;AACT;AA2DO,SAAS,kBACd,QACA,WACgC;AAIhC,QAAM,aAAuB,CAAC;AAG9B,MAAI;AAKJ,QAAM,aAAa;AAAA,IACjB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,aAAa;AAAA;AAAA,IAEb,eAAe;AAAA;AAAA,IAEf,oBAAoB;AAAA,IACpB,kBAAkB,oBAAI,IAAY;AAAA,EACpC;AAKA,QAAM,iBAAsC;AAAA,IAC1C,aAAa,oBAAI,IAAI;AAAA,IACrB,eAAe,oBAAI,IAAI;AAAA,IACvB,kBAAkB,oBAAI,IAAY;AAAA,IAClC,mBAAmB,oBAAI,IAAY;AAAA,IACnC,eAAe,oBAAI,IAAY;AAAA,IAC/B,qBAAqB,oBAAI,IAAY;AAAA,IACrC,qBAAqB,oBAAI,IAAI;AAAA,IAC7B,qBAAqB,oBAAI,IAAI;AAAA,IAC7B,aAAa;AAAA,IACb,uBAAuB,oBAAI,IAAoB;AAAA,IAC/C,kBAAkB,oBAAI,IAAY;AAAA,EACpC;AAKA,MAAI,aAA4D;AAKhE,QAAM,mBAAmB,MAA8B;AACrD,QAAI,OAAO,iBAAiB,QAAQ;AAClC,aAAQ,OAAkC,OAAO,aAAa,EAAE;AAAA,IAClE;AAIA,UAAM,SAAU,OAA0B,UAAU;AACpD,WAAO;AAAA,MACL,MAAM,OAAO;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,eAAO,EAAE,MAAM,MAAM;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,iBAAiB;AAKlC,QAAM,2BAA2B,CAC/B,uBACoD;AACpD,WAAO;AAAA,MACL,IAAI,cAAc;AAChB,eAAO,mBAAmB;AAAA,MAC5B;AAAA,MACA,OAAO,MAAM,mBAAmB,MAAM;AAAA,MACtC,OAAO,CAAC,MAAgB,mBAAmB,MAAM,CAAC;AAAA,MAClD,SAAS,CAAC,UAA0B;AApe1C;AAweQ,YAAI,aAAa,MAAM,SAAS,gBAAgB,MAAM,OAAO;AAC3D,qBAAW,KAAK,MAAM,KAAK;AAC3B,0BAAU,YAAV,mCAAoB,MAAM;AAC1B,0BAAU,WAAV,mCAAmB,MAAM;AAAA,QAC3B;AACA,2BAAmB,QAAQ,KAAK;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,IAAI,eAA+B;AAAA,IACxC,MAAM,MAAM,YAAY;AAnf5B;AAofM,cAAM,4CAAW,YAAX;AAEN,YAAM,oBAAoB,yBAAyB,UAAU;AAC7D,iBAAW,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEpC,UAAI;AACF,eAAO,MAAM;AACX,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,SAAS,KAAK;AAC5C,cAAI,KAAM;AAKV,cAAI,eAAe,MAAM;AACvB,gBAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,2BAAa;AAAA,YACf,WAAW,oBAAoB,KAAK,GAAG;AACrC,2BAAa;AAAA,YACf,OAAO;AACL,2BAAa;AAAA,YACf;AAAA,UACF;AAKA,cAAI,eAAe,SAAS;AAC1B;AAAA,cACE;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF,WAAW,eAAe,gBAAgB;AACxC;AAAA,cACE;AAAA,cAMA;AAAA,cACA;AAAA,YACF;AAAA,UACF,OAAO;AACL,kBAAM,aAAa;AACnB,kBAAM,CAAC,MAAM,IAAI,IAAI,oBAAoB,UAAU;AAEnD,gBAAI,SAAS,UAAU;AACrB,+BAAiB;AAAA,YACnB;AAEA;AAAA,cACE;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAKA,YAAI,eAAe,WAAW,eAAe,gBAAgB;AAC3D,cAAI,WAAW,kBAAkB;AAC/B,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,KAAI,gBAAW,uBAAX,YAAiC,WAAW;AAAA,YAClD,CAAC;AAAA,UACH;AACA,cAAI,WAAW,aAAa;AAI1B,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,KAAI,gBAAW,kBAAX,YAA4B,WAAW;AAAA,YAC7C,CAAC;AAAA,UACH;AACA,qBAAW,QAAQ,EAAE,MAAM,SAAS,CAAC;AAAA,QACvC,WAAW,eAAe,aAAa;AAMrC,qBAAW,CAAC,IAAI,IAAI,KAAK,eAAe,aAAa;AACnD,gBAAI,KAAK,MAAM;AACb,yBAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAAA,YAC7C;AACA,gBAAI,KAAK,WAAW;AAClB,yBAAW,QAAQ,EAAE,MAAM,iBAAiB,GAAG,CAAC;AAAA,YAClD;AAAA,UACF;AAKA,cAAI,eAAe,gBAAgB,MAAM;AACvC,uBAAW,QAAQ,EAAE,MAAM,cAAc,CAAC;AAAA,UAC5C;AACA,qBAAW,QAAQ,EAAE,MAAM,SAAS,CAAC;AAAA,QACvC;AAKA,gBAAM,4CAAW,YAAX,mCAAqB,WAAW,KAAK,EAAE;AAC7C,gBAAM,4CAAW,aAAX,mCAAsB;AAAA,MAC9B,SAAS,OAAO;AACd,cAAM,WACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAE1D,gBAAM,4CAAW,YAAX,mCAAqB,WAAW,KAAK,EAAE;AAE7C,YAAI,aAAa,KAAK,GAAG;AACvB,kBAAM,4CAAW,YAAX;AAAA,QACR,OAAO;AACL,kBAAM,4CAAW,YAAX,mCAAqB;AAAA,QAC7B;AAEA,mBAAW,QAAQ;AAAA,UACjB,MAAM;AAAA,UACN,WAAW,SAAS;AAAA,QACtB,CAAC;AAAA,MACH,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AE9mBA;AAAA,EACE;AAAA,OAEK;AAqCA,IAAM,+BAAN,MAEgC;AAAA,EAGrC,YAAY,SAA8C;AApD5D;AAqDI,SAAK,QAAQ,IAAI,YAAY;AAAA,MAC3B,GAAG;AAAA,MACH,UAAS,aAAQ,YAAR,YAAmB;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aACJ,SAOyC;AACzC,UAAM,eAAe,MAAM,eAAe,QAAQ,QAAQ;AAE1D,UAAM,SAAS,MAAM,KAAK,MAAM;AAAA,MAC9B,EAAE,UAAU,aAAa;AAAA,MACzB,EAAE,YAAY,CAAC,UAAU,UAAU,EAAE;AAAA,IACvC;AAEA,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,kBACJ,UAGgD;AAChD,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACF;","names":["providerMetadata"]}
1
+ {"version":3,"sources":["../src/adapter.ts","../src/utils.ts","../src/transport.ts"],"sourcesContent":["import {\n SystemMessage,\n type BaseMessage,\n type AIMessageChunk,\n} from '@langchain/core/messages';\nimport {\n convertToModelMessages,\n type UIMessage,\n type UIMessageChunk,\n type ModelMessage,\n} from 'ai';\nimport {\n convertToolResultPart,\n convertAssistantContent,\n convertUserContent,\n processModelChunk,\n processLangGraphEvent,\n parseLangGraphEvent,\n isToolResultPart,\n extractReasoningFromContentBlocks,\n extractCitationsFromContentBlocks,\n emitSourceChunks,\n} from './utils';\nimport type { LangGraphEventState } from './types';\nimport type { StreamCallbacks } from './stream-callbacks';\n\n/**\n * Options for converting a LangChain stream to an AI SDK UIMessageStream.\n */\nexport interface ToUIMessageStreamOptions<\n TState = unknown,\n> extends StreamCallbacks<TState> {\n /**\n * Whether to emit the outer `start` chunk.\n *\n * @default true\n */\n sendStart?: boolean;\n\n /**\n * Whether to emit the outer `finish` chunk.\n *\n * @default true\n */\n sendFinish?: boolean;\n}\n\n/**\n * Converts AI SDK UIMessages to LangChain BaseMessage objects.\n *\n * This function transforms the AI SDK's message format into LangChain's message\n * format, enabling seamless integration between the two frameworks.\n *\n * @param messages - Array of AI SDK UIMessage objects to convert.\n * @returns Promise resolving to an array of LangChain BaseMessage objects.\n *\n * @example\n * ```ts\n * import { toBaseMessages } from '@ai-sdk/langchain';\n *\n * const langchainMessages = await toBaseMessages(uiMessages);\n *\n * // Use with LangChain\n * const response = await model.invoke(langchainMessages);\n * ```\n */\nexport async function toBaseMessages(\n messages: UIMessage[],\n): Promise<BaseMessage[]> {\n const modelMessages = await convertToModelMessages(messages);\n return convertModelMessages(modelMessages);\n}\n\n/**\n * Converts ModelMessages to LangChain BaseMessage objects.\n *\n * @param modelMessages - Array of ModelMessage objects from convertToModelMessages.\n * @returns Array of LangChain BaseMessage objects.\n */\nexport function convertModelMessages(\n modelMessages: ModelMessage[],\n): BaseMessage[] {\n const result: BaseMessage[] = [];\n\n for (const message of modelMessages) {\n switch (message.role) {\n case 'tool': {\n // Tool messages contain an array of tool results\n for (const item of message.content) {\n if (isToolResultPart(item)) {\n result.push(convertToolResultPart(item));\n }\n }\n break;\n }\n\n case 'assistant': {\n result.push(convertAssistantContent(message.content));\n break;\n }\n\n case 'system': {\n result.push(new SystemMessage({ content: message.content }));\n break;\n }\n\n case 'user': {\n result.push(convertUserContent(message.content));\n break;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Type guard to check if a value is a streamEvents event object.\n * streamEvents produces objects with `event` and `data` properties.\n *\n * @param value - The value to check.\n * @returns True if the value is a streamEvents event object.\n */\nfunction isStreamEventsEvent(\n value: unknown,\n): value is { event: string; data: Record<string, unknown> } {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n // Check for event property being a string\n if (!('event' in obj) || typeof obj.event !== 'string') return false;\n // Check for data property being an object (but allow null/undefined)\n if (!('data' in obj)) return false;\n // data can be null in some events, treat as empty object\n return obj.data === null || typeof obj.data === 'object';\n}\n\n/**\n * Processes a streamEvents event and emits UI message chunks.\n *\n * @param event - The streamEvents event to process.\n * @param state - The state for tracking stream progress.\n * @param controller - The controller to emit UI message chunks.\n */\nfunction processStreamEventsEvent(\n event: {\n event: string;\n data: Record<string, unknown> | null;\n run_id?: string;\n name?: string;\n },\n state: {\n started: boolean;\n messageId: string;\n reasoningStarted: boolean;\n textStarted: boolean;\n textMessageId: string | null;\n reasoningMessageId: string | null;\n emittedSourceIds: Set<string>;\n },\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n /**\n * Capture run_id from event level if available (streamEvents v2 format)\n */\n if (event.run_id && !state.started) {\n state.messageId = event.run_id;\n }\n\n /**\n * Skip events with null/undefined data\n */\n if (!event.data) return;\n\n switch (event.event) {\n case 'on_chat_model_start': {\n /**\n * End the previous model turn's reasoning stream before a new LLM invocation.\n * Without this, reasoning-delta chunks from the next turn could attach to the\n * wrong reasoning part in the UI message stream.\n */\n if (state.reasoningStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id:\n state.reasoningMessageId != null\n ? state.reasoningMessageId\n : state.messageId,\n });\n state.reasoningStarted = false;\n state.reasoningMessageId = null;\n }\n\n /**\n * End the previous model turn's text stream before a new LLM invocation.\n * The streamEvents adapter otherwise leaves `textStarted` true, so all later\n * text-delta chunks append to the first text part — tools still grow the parts\n * array, which breaks chronological order in the assistant message.\n */\n if (state.textStarted) {\n controller.enqueue({\n type: 'text-end',\n id:\n state.textMessageId != null ? state.textMessageId : state.messageId,\n });\n state.textStarted = false;\n state.textMessageId = null;\n }\n\n /**\n * Handle model start — capture message metadata if available.\n * `run_id` is on the event in streamEvents v2; fall back to `data` for compatibility.\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n if (runId) {\n state.messageId = runId;\n }\n break;\n }\n\n case 'on_chat_model_stream': {\n /**\n * Handle streaming token chunks\n */\n const chunk = event.data.chunk;\n if (chunk && typeof chunk === 'object') {\n /**\n * Get message ID from chunk if available\n */\n const chunkId = (chunk as { id?: string }).id;\n if (chunkId) {\n state.messageId = chunkId;\n }\n\n /**\n * Handle reasoning content from contentBlocks\n */\n const reasoning = extractReasoningFromContentBlocks(chunk);\n if (reasoning) {\n if (!state.reasoningStarted) {\n // Track the ID used for reasoning-start to ensure reasoning-end uses the same ID\n state.reasoningMessageId = state.messageId;\n controller.enqueue({\n type: 'reasoning-start',\n id: state.messageId,\n });\n state.reasoningStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: state.reasoningMessageId ?? state.messageId,\n });\n }\n\n /**\n * Extract text content from chunk\n */\n const content = (chunk as { content?: unknown }).content;\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n typeof c === 'object' &&\n c !== null &&\n 'type' in c &&\n c.type === 'text',\n )\n .map(c => c.text)\n .join('')\n : '';\n\n if (text) {\n /**\n * If reasoning was streamed before text, close reasoning first\n */\n if (state.reasoningStarted && !state.textStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: state.reasoningMessageId ?? state.messageId,\n });\n state.reasoningStarted = false;\n }\n\n if (!state.textStarted) {\n // Track the ID used for text-start to ensure text-end uses the same ID\n state.textMessageId = state.messageId;\n controller.enqueue({ type: 'text-start', id: state.messageId });\n state.textStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: state.textMessageId ?? state.messageId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(chunk);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n state.messageId,\n state.emittedSourceIds,\n controller,\n );\n }\n }\n break;\n }\n\n case 'on_tool_start': {\n /**\n * Handle tool call start\n * run_id and name are at event level in v2, check data for backwards compatibility\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n const name = event.name || (event.data.name as string | undefined);\n\n if (runId && name) {\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: runId,\n toolName: name,\n dynamic: true,\n });\n }\n break;\n }\n\n case 'on_tool_end': {\n /**\n * Handle tool call end\n * run_id is at event level in v2, check data for backwards compatibility\n */\n const runId = event.run_id || (event.data.run_id as string | undefined);\n const output = event.data.output;\n\n if (runId) {\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId: runId,\n output,\n });\n }\n break;\n }\n }\n}\n\n/**\n * Checks if an error is an abort error.\n */\nfunction isAbortError(error: unknown): boolean {\n if (error instanceof Error) {\n return (\n error.name === 'AbortError' ||\n (error instanceof DOMException && error.name === 'AbortError')\n );\n }\n return false;\n}\n\n/**\n * Converts a LangChain stream to an AI SDK UIMessageStream.\n *\n * This function automatically detects the stream type and handles:\n * - Direct model streams (AsyncIterable from `model.stream()`)\n * - LangGraph streams (ReadableStream with `streamMode: ['values', 'messages']`)\n * - streamEvents streams (from `agent.streamEvents()` or `model.streamEvents()`)\n *\n * @param stream - A stream from LangChain model.stream(), graph.stream(), or streamEvents().\n * @param options - Optional lifecycle callbacks and outer chunk controls.\n * @returns A ReadableStream of UIMessageChunk objects.\n *\n * @example\n * ```ts\n * // With a direct model stream\n * const model = new ChatOpenAI({ model: 'gpt-4o-mini' });\n * const stream = await model.stream(messages);\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(stream),\n * });\n *\n * // With a LangGraph stream\n * const graphStream = await graph.stream(\n * { messages },\n * { streamMode: ['values', 'messages'] }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(graphStream),\n * });\n *\n * // With streamEvents\n * const streamEvents = agent.streamEvents(\n * { messages },\n * { version: \"v2\" }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream(streamEvents),\n * });\n *\n * // With callbacks for LangGraph state\n * const graphStream = await graph.stream(\n * { messages },\n * { streamMode: ['values', 'messages'] }\n * );\n * return createUIMessageStreamResponse({\n * stream: toUIMessageStream<MyStateType>(graphStream, {\n * onFinish: async (finalState) => {\n * if (finalState) {\n * await saveToDatabase(finalState);\n * }\n * },\n * onError: (error) => console.error('Stream failed:', error),\n * onAbort: () => console.log('Stream aborted'),\n * }),\n * });\n * ```\n */\nexport function toUIMessageStream<TState = unknown>(\n stream: AsyncIterable<AIMessageChunk> | ReadableStream,\n options?: ToUIMessageStreamOptions<TState>,\n): ReadableStream<UIMessageChunk> {\n const { sendStart = true, sendFinish = true } = options ?? {};\n\n /**\n * Track text chunks for onFinal callback\n */\n const textChunks: string[] = [];\n\n /** Last LangGraph values event data for onFinish callback */\n let lastValuesData: TState | undefined;\n\n /**\n * State for model stream handling\n */\n const modelState = {\n started: false,\n messageId: 'langchain-msg-1',\n reasoningStarted: false,\n textStarted: false,\n /** Track the ID used for text-start to ensure text-end uses the same ID */\n textMessageId: null as string | null,\n /** Track the ID used for reasoning-start to ensure reasoning-end uses the same ID */\n reasoningMessageId: null as string | null,\n emittedSourceIds: new Set<string>(),\n };\n\n /**\n * State for LangGraph stream handling\n */\n const langGraphState: LangGraphEventState = {\n messageSeen: new Map(),\n messageConcat: new Map(),\n emittedToolCalls: new Set<string>(),\n emittedToolInputs: new Set<string>(),\n emittedImages: new Set<string>(),\n emittedReasoningIds: new Set<string>(),\n messageReasoningIds: new Map(),\n toolCallInfoByIndex: new Map(),\n currentStep: null as number | null,\n emittedToolCallsByKey: new Map<string, string>(),\n emittedSourceIds: new Set<string>(),\n };\n\n /**\n * Track detected stream type: null = not yet detected\n */\n let streamType: 'model' | 'langgraph' | 'streamEvents' | null = null;\n\n /**\n * Get async iterator from the stream (works for both AsyncIterable and ReadableStream)\n */\n const getAsyncIterator = (): AsyncIterator<unknown> => {\n if (Symbol.asyncIterator in stream) {\n return (stream as AsyncIterable<unknown>)[Symbol.asyncIterator]();\n }\n /**\n * For ReadableStream without Symbol.asyncIterator\n */\n const reader = (stream as ReadableStream).getReader();\n return {\n async next() {\n const { done, value } = await reader.read();\n return { done, value };\n },\n };\n };\n\n const iterator = getAsyncIterator();\n\n /**\n * Create a wrapper around the controller to intercept text chunks for callbacks\n */\n const createCallbackController = (\n originalController: ReadableStreamDefaultController<UIMessageChunk>,\n ): ReadableStreamDefaultController<UIMessageChunk> => {\n return {\n get desiredSize() {\n return originalController.desiredSize;\n },\n close: () => originalController.close(),\n error: (e?: unknown) => originalController.error(e),\n enqueue: (chunk: UIMessageChunk) => {\n /**\n * Intercept text-delta chunks for callbacks\n */\n if (chunk.type === 'text-delta' && chunk.delta) {\n if (options?.onFinal) {\n textChunks.push(chunk.delta);\n }\n options?.onToken?.(chunk.delta);\n options?.onText?.(chunk.delta);\n }\n originalController.enqueue(chunk);\n },\n };\n };\n\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n await options?.onStart?.();\n\n const wrappedController = createCallbackController(controller);\n if (sendStart) {\n controller.enqueue({ type: 'start' });\n }\n\n try {\n while (true) {\n const { done, value } = await iterator.next();\n if (done) break;\n\n /**\n * Detect stream type on first value\n */\n if (streamType === null) {\n if (Array.isArray(value)) {\n streamType = 'langgraph';\n } else if (isStreamEventsEvent(value)) {\n streamType = 'streamEvents';\n } else {\n streamType = 'model';\n }\n }\n\n /**\n * Process based on detected type\n */\n if (streamType === 'model') {\n processModelChunk(\n value as AIMessageChunk,\n modelState,\n wrappedController,\n );\n } else if (streamType === 'streamEvents') {\n processStreamEventsEvent(\n value as {\n event: string;\n data: Record<string, unknown> | null;\n run_id?: string;\n name?: string;\n },\n modelState,\n wrappedController,\n );\n } else {\n const eventArray = value as unknown[];\n const [type, data] = parseLangGraphEvent(eventArray);\n\n if (type === 'values') {\n lastValuesData = data as TState;\n }\n\n processLangGraphEvent(\n eventArray,\n langGraphState,\n wrappedController,\n );\n }\n }\n\n /**\n * Finalize based on stream type\n */\n if (streamType === 'model' || streamType === 'streamEvents') {\n if (modelState.reasoningStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: modelState.reasoningMessageId ?? modelState.messageId,\n });\n }\n if (modelState.textStarted) {\n /**\n * Use the same ID that was used for text-start\n */\n controller.enqueue({\n type: 'text-end',\n id: modelState.textMessageId ?? modelState.messageId,\n });\n }\n if (sendFinish) {\n controller.enqueue({ type: 'finish' });\n }\n } else if (streamType === 'langgraph') {\n /**\n * Close any open text/reasoning parts before finishing.\n * This handles streams without values events (e.g. streamMode: 'messages')\n * where the values handler never ran to emit *-end events.\n */\n for (const [id, seen] of langGraphState.messageSeen) {\n if (seen.text) {\n controller.enqueue({ type: 'text-end', id });\n }\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n }\n\n /**\n * Emit finish-step if a step was started\n */\n if (langGraphState.currentStep !== null) {\n controller.enqueue({ type: 'finish-step' });\n }\n if (sendFinish) {\n controller.enqueue({ type: 'finish' });\n }\n }\n\n /**\n * Call onFinal callback with aggregated text\n */\n await options?.onFinal?.(textChunks.join(''));\n await options?.onFinish?.(lastValuesData);\n } catch (error) {\n const errorObj =\n error instanceof Error ? error : new Error(String(error));\n\n await options?.onFinal?.(textChunks.join(''));\n\n if (isAbortError(error)) {\n await options?.onAbort?.();\n } else {\n await options?.onError?.(errorObj);\n }\n\n controller.enqueue({\n type: 'error',\n errorText: errorObj.message,\n });\n } finally {\n controller.close();\n }\n },\n });\n}\n","import {\n AIMessage,\n HumanMessage,\n ToolMessage,\n AIMessageChunk,\n type ContentBlock,\n type ToolCall,\n type BaseMessage,\n type BaseMessageChunk,\n type ToolCallChunk,\n} from '@langchain/core/messages';\nimport type {\n UIMessageChunk,\n ToolResultPart,\n AssistantContent,\n UserContent,\n ProviderMetadata,\n JSONValue,\n} from 'ai';\n\nimport type {\n LangGraphEventState,\n LangGraphMessageSeen,\n ReasoningContentBlock,\n ThinkingContentBlock,\n GPT5ReasoningOutput,\n ImageGenerationOutput,\n NormalizedCitation,\n} from './types';\n\n/**\n * Parses a LangGraph event tuple into [type, data].\n * Handles both 2-element [type, data] and 3-element [namespace, type, data] formats.\n *\n * @param event - The raw LangGraph event array.\n * @returns A tuple of [type, data].\n */\nexport function parseLangGraphEvent(\n event: unknown[],\n): [type: unknown, data: unknown] {\n return event.length === 3 ? [event[1], event[2]] : [event[0], event[1]];\n}\n\n/**\n * Converts a ToolResultPart to a LangChain ToolMessage\n * @param block - The ToolResultPart to convert.\n * @returns The converted ToolMessage.\n */\nexport function convertToolResultPart(block: ToolResultPart): ToolMessage {\n const content = (() => {\n if (block.output.type === 'text' || block.output.type === 'error-text') {\n return block.output.value;\n }\n\n if (block.output.type === 'json' || block.output.type === 'error-json') {\n return JSON.stringify(block.output.value);\n }\n\n if (block.output.type === 'content') {\n return block.output.value\n .map(outputBlock => {\n if (outputBlock.type === 'text') {\n return outputBlock.text;\n }\n return '';\n })\n .join('');\n }\n\n return '';\n })();\n\n return new ToolMessage({\n tool_call_id: block.toolCallId,\n content,\n });\n}\n\n/**\n * Converts AssistantContent to LangChain AIMessage\n * @param content - The AssistantContent to convert.\n * @returns The converted AIMessage.\n */\nexport function convertAssistantContent(content: AssistantContent): AIMessage {\n if (typeof content === 'string') {\n return new AIMessage({ content });\n }\n\n const textParts: string[] = [];\n const toolCalls: Array<{\n id: string;\n name: string;\n args: Record<string, unknown>;\n }> = [];\n\n for (const part of content) {\n if (part.type === 'text') {\n textParts.push(part.text);\n } else if (part.type === 'tool-call') {\n toolCalls.push({\n id: part.toolCallId,\n name: part.toolName,\n args: part.input as Record<string, unknown>,\n });\n }\n }\n\n return new AIMessage({\n content: textParts.join(''),\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n });\n}\n\n/**\n * Helper to generate a default filename from mediaType\n */\nfunction getDefaultFilename(\n mediaType: string,\n prefix: string = 'file',\n): string {\n const fileExtension = mediaType.split('/')[1] || 'bin';\n return `${prefix}.${fileExtension}`;\n}\n\n/**\n * OpenAI-native content block type for images.\n * This format is passed through directly by ChatOpenAI to OpenAI's API.\n */\ntype OpenAIImageBlock = {\n type: 'image_url';\n image_url: {\n url: string;\n detail?: 'auto' | 'low' | 'high';\n };\n};\n\n/**\n * Content block type for HumanMessage that supports both text and OpenAI images.\n */\ntype HumanMessageContentBlock =\n | { type: 'text'; text: string }\n | OpenAIImageBlock\n | ContentBlock;\n\n/**\n * Converts UserContent to LangChain HumanMessage\n * @param content - The UserContent to convert.\n * @returns The converted HumanMessage.\n */\nexport function convertUserContent(content: UserContent): HumanMessage {\n if (typeof content === 'string') {\n return new HumanMessage({ content });\n }\n\n const contentBlocks: HumanMessageContentBlock[] = [];\n\n for (const part of content) {\n if (part.type === 'text') {\n contentBlocks.push({ type: 'text', text: part.text });\n } else if (part.type === 'image') {\n const imagePart = part as {\n type: 'image';\n image: string | Uint8Array | URL | ArrayBuffer;\n mediaType?: string;\n };\n\n /**\n * Use OpenAI's native image_url format which is passed through directly\n * handle URL objects\n */\n if (imagePart.image instanceof URL) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: imagePart.image.toString() },\n });\n } else if (typeof imagePart.image === 'string') {\n /**\n * Handle string (could be URL or base64)\n */\n /**\n * Check if it's a URL (including data: URLs)\n */\n if (\n imagePart.image.startsWith('http://') ||\n imagePart.image.startsWith('https://') ||\n imagePart.image.startsWith('data:')\n ) {\n /**\n * OpenAI accepts both http URLs and data URLs directly\n */\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: imagePart.image },\n });\n } else {\n /**\n * Assume base64 encoded data - wrap in data URL\n */\n const mimeType = imagePart.mediaType || 'image/png';\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${mimeType};base64,${imagePart.image}` },\n });\n }\n } else if (\n /**\n * Handle Uint8Array or ArrayBuffer (binary data)\n */\n imagePart.image instanceof Uint8Array ||\n imagePart.image instanceof ArrayBuffer\n ) {\n const bytes =\n imagePart.image instanceof ArrayBuffer\n ? new Uint8Array(imagePart.image)\n : imagePart.image;\n /**\n * Convert to base64 data URL\n */\n const base64 = btoa(String.fromCharCode(...bytes));\n const mimeType = imagePart.mediaType || 'image/png';\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${mimeType};base64,${base64}` },\n });\n }\n } else if (part.type === 'file') {\n const rawFilePart = part as {\n type: 'file';\n data:\n | string\n | Uint8Array\n | URL\n | ArrayBuffer\n | { type: 'data'; data: string | Uint8Array | ArrayBuffer }\n | { type: 'url'; url: URL }\n | { type: 'reference'; reference: Record<string, string> }\n | { type: 'text'; text: string };\n mediaType: string;\n filename?: string;\n };\n\n // Normalize tagged data shape into the legacy bare value this code expects.\n const normalizedData: string | Uint8Array | URL | ArrayBuffer = (() => {\n const data = rawFilePart.data;\n if (\n typeof data === 'object' &&\n data !== null &&\n !(data instanceof URL) &&\n !(data instanceof Uint8Array) &&\n !(data instanceof ArrayBuffer) &&\n 'type' in data\n ) {\n switch (data.type) {\n case 'data':\n return data.data;\n case 'url':\n return data.url;\n case 'text':\n return data.text;\n default:\n return '';\n }\n }\n return data as string | Uint8Array | URL | ArrayBuffer;\n })();\n\n const filePart = {\n type: 'file' as const,\n data: normalizedData,\n mediaType: rawFilePart.mediaType,\n filename: rawFilePart.filename,\n };\n\n /**\n * Check if this is an image file - if so, use OpenAI's image_url format\n */\n const isImage = filePart.mediaType?.startsWith('image/');\n\n if (isImage) {\n /**\n * Handle image files using OpenAI's native image_url format\n */\n if (filePart.data instanceof URL) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: filePart.data.toString() },\n });\n } else if (typeof filePart.data === 'string') {\n /**\n * URLs (including data URLs) can be passed directly\n */\n if (\n filePart.data.startsWith('http://') ||\n filePart.data.startsWith('https://') ||\n filePart.data.startsWith('data:')\n ) {\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: filePart.data },\n });\n } else {\n /**\n * Assume base64 - wrap in data URL\n */\n contentBlocks.push({\n type: 'image_url',\n image_url: {\n url: `data:${filePart.mediaType};base64,${filePart.data}`,\n },\n });\n }\n } else if (\n filePart.data instanceof Uint8Array ||\n filePart.data instanceof ArrayBuffer\n ) {\n const bytes =\n filePart.data instanceof ArrayBuffer\n ? new Uint8Array(filePart.data)\n : filePart.data;\n const base64 = btoa(String.fromCharCode(...bytes));\n contentBlocks.push({\n type: 'image_url',\n image_url: { url: `data:${filePart.mediaType};base64,${base64}` },\n });\n }\n } else {\n // Handle non-image files using LangChain's ContentBlock format\n const filename =\n filePart.filename || getDefaultFilename(filePart.mediaType, 'file');\n\n if (filePart.data instanceof URL) {\n contentBlocks.push({\n type: 'file',\n url: filePart.data.toString(),\n mimeType: filePart.mediaType,\n filename,\n });\n } else if (typeof filePart.data === 'string') {\n if (\n filePart.data.startsWith('http://') ||\n filePart.data.startsWith('https://')\n ) {\n contentBlocks.push({\n type: 'file',\n url: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n } else if (filePart.data.startsWith('data:')) {\n const matches = filePart.data.match(/^data:([^;]+);base64,(.+)$/);\n if (matches) {\n contentBlocks.push({\n type: 'file',\n data: matches[2],\n mimeType: matches[1],\n filename,\n });\n } else {\n contentBlocks.push({\n type: 'file',\n url: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n } else {\n contentBlocks.push({\n type: 'file',\n data: filePart.data,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n } else if (\n filePart.data instanceof Uint8Array ||\n filePart.data instanceof ArrayBuffer\n ) {\n const bytes =\n filePart.data instanceof ArrayBuffer\n ? new Uint8Array(filePart.data)\n : filePart.data;\n const base64 = btoa(String.fromCharCode(...bytes));\n contentBlocks.push({\n type: 'file',\n data: base64,\n mimeType: filePart.mediaType,\n filename,\n });\n }\n }\n }\n }\n\n /**\n * If we only have text parts, join them as a simple string for efficiency\n */\n if (contentBlocks.every(block => block.type === 'text')) {\n return new HumanMessage({\n content: contentBlocks\n .map(block => (block as unknown as { text: string }).text)\n .join(''),\n });\n }\n\n return new HumanMessage({ content: contentBlocks });\n}\n\n/**\n * Helper to check if a content item is a ToolResultPart\n * @param item - The item to check.\n * @returns True if the item is a ToolResultPart, false otherwise.\n */\nexport function isToolResultPart(item: unknown): item is ToolResultPart {\n return (\n item != null &&\n typeof item === 'object' &&\n 'type' in item &&\n (item as { type: string }).type === 'tool-result'\n );\n}\n\n/**\n * Processes a model stream chunk and emits UI message chunks.\n * @param chunk - The AIMessageChunk to process.\n * @param state - The state of the model stream.\n * @param controller - The controller to use to emit the UI message chunks.\n */\nexport function processModelChunk(\n chunk: AIMessageChunk,\n state: {\n started: boolean;\n messageId: string;\n reasoningStarted?: boolean;\n textStarted?: boolean;\n /** Track the ID used for reasoning-start to ensure reasoning-end uses the same ID */\n reasoningMessageId?: string | null;\n /** Track the ID used for text-start to ensure text-end uses the same ID */\n textMessageId?: string | null;\n emittedImages?: Set<string>;\n emittedSourceIds?: Set<string>;\n },\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n /**\n * Initialize emittedImages set if not present\n */\n if (!state.emittedImages) {\n state.emittedImages = new Set<string>();\n }\n\n if (!state.emittedSourceIds) {\n state.emittedSourceIds = new Set<string>();\n }\n\n /**\n * Get the message ID from the chunk if available\n */\n if (chunk.id) {\n state.messageId = chunk.id;\n }\n\n /**\n * Handle image generation outputs from additional_kwargs.tool_outputs\n */\n const chunkObj = chunk as unknown as Record<string, unknown>;\n const additionalKwargs = chunkObj.additional_kwargs as\n | Record<string, unknown>\n | undefined;\n const imageOutputs = extractImageOutputs(additionalKwargs);\n\n for (const imageOutput of imageOutputs) {\n /**\n * Only emit if we have image data and haven't emitted this image yet\n */\n if (imageOutput.result && !state.emittedImages.has(imageOutput.id)) {\n state.emittedImages.add(imageOutput.id);\n\n /**\n * Emit as a file part using proper AI SDK multimodal format\n */\n const mediaType = `image/${imageOutput.output_format || 'png'}`;\n controller.enqueue({\n type: 'file',\n mediaType,\n url: `data:${mediaType};base64,${imageOutput.result}`,\n });\n state.started = true;\n }\n }\n\n /**\n * Handle reasoning content from contentBlocks or response_metadata.output\n * For direct model streams, we check both sources since there's no values event\n * that would cause duplication (unlike LangGraph streams)\n */\n const reasoning =\n extractReasoningFromContentBlocks(chunk) ||\n extractReasoningFromValuesMessage(chunk);\n if (reasoning) {\n if (!state.reasoningStarted) {\n // Track the ID used for reasoning-start to ensure subsequent chunks use the same ID\n state.reasoningMessageId = state.messageId;\n controller.enqueue({ type: 'reasoning-start', id: state.messageId });\n state.reasoningStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: state.reasoningMessageId ?? state.messageId,\n });\n }\n\n /**\n * Extract text content from AIMessageChunk\n */\n const text =\n typeof chunk.content === 'string'\n ? chunk.content\n : Array.isArray(chunk.content)\n ? chunk.content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n typeof c === 'object' &&\n c !== null &&\n 'type' in c &&\n c.type === 'text',\n )\n .map(c => c.text)\n .join('')\n : '';\n\n if (text) {\n /**\n * If reasoning was streamed before text, close reasoning first\n */\n if (state.reasoningStarted && !state.textStarted) {\n controller.enqueue({\n type: 'reasoning-end',\n id: state.reasoningMessageId ?? state.messageId,\n });\n state.reasoningStarted = false;\n }\n\n if (!state.textStarted) {\n // Track the ID used for text-start to ensure subsequent chunks use the same ID\n state.textMessageId = state.messageId;\n controller.enqueue({ type: 'text-start', id: state.messageId });\n state.textStarted = true;\n state.started = true;\n }\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: state.textMessageId ?? state.messageId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(chunk);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n state.messageId,\n state.emittedSourceIds,\n controller,\n );\n }\n}\n\n/**\n * Checks if a message is a plain object (not a LangChain class instance).\n * LangChain class instances have a _getType method.\n *\n * @param msg - The message to check.\n * @returns True if the message is a plain object, false otherwise.\n */\nexport function isPlainMessageObject(msg: unknown): boolean {\n if (msg == null || typeof msg !== 'object') return false;\n /**\n * LangChain class instances have _getType method\n */\n return typeof (msg as { _getType?: unknown })._getType !== 'function';\n}\n\n/**\n * Extracts the actual message ID from a message.\n * Handles both class instances (msg.id) and serialized LangChain messages (msg.kwargs.id).\n *\n * @param msg - The message to extract the ID from.\n * @returns The message ID string, or undefined if not found.\n */\nexport function getMessageId(msg: unknown): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n const msgObj = msg as Record<string, unknown>;\n\n /**\n * For class instances, id is directly on the object\n */\n if (typeof msgObj.id === 'string') {\n return msgObj.id;\n }\n\n /**\n * For serialized LangChain messages, id is in kwargs\n */\n if (\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ) {\n const kwargs = msgObj.kwargs as Record<string, unknown>;\n if (typeof kwargs.id === 'string') {\n return kwargs.id;\n }\n }\n\n return undefined;\n}\n\n/**\n * Checks if a message is an AI message chunk (works for both class instances and plain objects).\n * For class instances, only AIMessageChunk is matched (not AIMessage).\n * For plain objects from RemoteGraph API, matches type === 'ai' (TypeScript langchain-core)\n * or type === 'AIMessageChunk' (Python langchain-core).\n * For serialized LangChain messages, matches type === 'constructor' with AIMessageChunk in id path.\n *\n * @param msg - The message to check.\n * @returns True if the message is an AI message chunk, false otherwise.\n */\nexport function isAIMessageChunk(\n msg: unknown,\n): msg is AIMessageChunk & { type?: string; content?: string } {\n /**\n * Actual AIMessageChunk class instance\n */\n if (AIMessageChunk.isInstance(msg)) return true;\n /**\n * Plain object from RemoteGraph API (not a LangChain class instance)\n */\n if (isPlainMessageObject(msg)) {\n const messageRecord = msg as Record<string, unknown>;\n /**\n * Direct type from RemoteGraph format. TypeScript langchain-core emits\n * type === 'ai'; Python langchain-core emits type === 'AIMessageChunk'.\n */\n if (\n 'type' in messageRecord &&\n (messageRecord.type === 'ai' || messageRecord.type === 'AIMessageChunk')\n ) {\n return true;\n }\n /**\n * Serialized LangChain message format: { lc: 1, type: \"constructor\", id: [\"...\", \"AIMessageChunk\"], kwargs: {...} }\n */\n if (\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n (messageRecord.id.includes('AIMessageChunk') ||\n messageRecord.id.includes('AIMessage'))\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Checks if a message is a Tool message (works for both class instances and plain objects).\n *\n * @param msg - The message to check.\n * @returns True if the message is a Tool message, false otherwise.\n */\nexport function isToolMessageType(\n msg: unknown,\n): msg is ToolMessage & { type?: string; tool_call_id?: string } {\n if (ToolMessage.isInstance(msg)) return true;\n /**\n * Plain object from RemoteGraph API (not a LangChain class instance)\n */\n if (isPlainMessageObject(msg)) {\n const messageRecord = msg as Record<string, unknown>;\n /**\n * Direct type === 'tool' (RemoteGraph format)\n */\n if ('type' in messageRecord && messageRecord.type === 'tool') return true;\n /**\n * Serialized LangChain message format\n */\n if (\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n messageRecord.id.includes('ToolMessage')\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Gets text content from a message (works for both class instances and plain objects).\n *\n * @param msg - The message to get the text from.\n * @returns The text content of the message.\n */\nexport function getMessageText(msg: unknown): string {\n if (AIMessageChunk.isInstance(msg)) {\n return msg.text ?? '';\n }\n\n if (msg == null || typeof msg !== 'object') return '';\n\n const msgObj = msg as Record<string, unknown>;\n\n // For serialized LangChain messages, content is in kwargs\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n if ('content' in dataSource) {\n const content = dataSource.content;\n /**\n * Handle string content\n */\n if (typeof content === 'string') {\n return content;\n }\n /**\n * Handle array of content blocks (e.g., [{ type: 'text', text: 'The', index: 0 }])\n */\n if (Array.isArray(content)) {\n return content\n .filter(\n (block): block is { type: 'text'; text: string } =>\n block != null &&\n typeof block === 'object' &&\n block.type === 'text' &&\n typeof block.text === 'string',\n )\n .map(block => block.text)\n .join('');\n }\n return '';\n }\n return '';\n}\n\n/**\n * Checks if an object is a reasoning content block\n *\n * @param obj - The object to check.\n * @returns True if the object is a reasoning content block, false otherwise.\n */\nexport function isReasoningContentBlock(\n obj: unknown,\n): obj is ReasoningContentBlock {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'reasoning' &&\n 'reasoning' in obj &&\n typeof (obj as { reasoning: unknown }).reasoning === 'string'\n );\n}\n\n/**\n * Checks if an object is a thinking content block (Anthropic-style)\n *\n * @param obj - The object to check.\n * @returns True if the object is a thinking content block, false otherwise.\n */\nexport function isThinkingContentBlock(\n obj: unknown,\n): obj is ThinkingContentBlock {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'thinking' &&\n 'thinking' in obj &&\n typeof (obj as { thinking: unknown }).thinking === 'string'\n );\n}\n\n/**\n * Checks if an object is a GPT-5 reasoning output block\n */\nfunction isGPT5ReasoningOutput(obj: unknown): obj is GPT5ReasoningOutput {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'reasoning' &&\n 'summary' in obj &&\n Array.isArray((obj as { summary: unknown }).summary)\n );\n}\n\n/**\n * Extracts the reasoning block ID from a message (GPT-5 format).\n * This ID is consistent across streaming and values events.\n * Handles both class instances and serialized LangChain message objects.\n *\n * @param msg - The message to extract the reasoning ID from.\n * @returns The reasoning block ID if found, undefined otherwise.\n */\nexport function extractReasoningId(msg: unknown): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check additional_kwargs.reasoning.id (GPT-5 streaming format)\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { id?: string } } }\n ).additional_kwargs;\n if (additionalKwargs?.reasoning?.id) {\n return additionalKwargs.reasoning.id;\n }\n\n // Check response_metadata.output for reasoning block ID (GPT-5 final format)\n const responseMetadata = (\n kwargs as { response_metadata?: { output?: unknown[] } }\n ).response_metadata;\n if (responseMetadata && Array.isArray(responseMetadata.output)) {\n for (const item of responseMetadata.output) {\n if (isGPT5ReasoningOutput(item)) {\n return item.id;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Extracts reasoning content from contentBlocks or additional_kwargs.reasoning.summary\n *\n * IMPORTANT: This function is designed for STREAMING chunks where content is delta-based.\n * It does NOT extract from response_metadata.output because that contains accumulated\n * content (not deltas) and would cause duplication during streaming.\n *\n * For non-streaming/values events, use extractReasoningFromValuesMessage instead.\n *\n * Handles both class instances and serialized LangChain message objects.\n *\n * @param msg - The message to extract reasoning from.\n * @returns The reasoning text if found, undefined otherwise.\n */\nexport function extractReasoningFromContentBlocks(\n msg: unknown,\n): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check contentBlocks (Anthropic-style) - highest priority\n const contentBlocks = (kwargs as { contentBlocks?: unknown[] }).contentBlocks;\n if (Array.isArray(contentBlocks)) {\n const reasoningParts: string[] = [];\n for (const block of contentBlocks) {\n if (isReasoningContentBlock(block)) {\n reasoningParts.push(block.reasoning);\n } else if (isThinkingContentBlock(block)) {\n reasoningParts.push(block.thinking);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n // Check additional_kwargs.reasoning.summary (GPT-5 streaming format)\n // This contains DELTA content during streaming, not accumulated content\n // Format can be either { type: \"summary_text\", text: \"...\" } or just { text: \"...\" }\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { summary?: unknown[] } } }\n ).additional_kwargs;\n if (\n additionalKwargs?.reasoning &&\n Array.isArray(additionalKwargs.reasoning.summary)\n ) {\n const reasoningParts: string[] = [];\n for (const summaryItem of additionalKwargs.reasoning.summary) {\n if (\n typeof summaryItem === 'object' &&\n summaryItem !== null &&\n 'text' in summaryItem &&\n typeof (summaryItem as { text: unknown }).text === 'string'\n ) {\n reasoningParts.push((summaryItem as { text: string }).text);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n return undefined;\n}\n\n/**\n * Extracts reasoning content from a values event message.\n * This checks response_metadata.output which contains the full accumulated reasoning.\n *\n * @param msg - The message to extract reasoning from.\n * @returns The reasoning text if found, undefined otherwise.\n */\nexport function extractReasoningFromValuesMessage(\n msg: unknown,\n): string | undefined {\n if (msg == null || typeof msg !== 'object') return undefined;\n\n // For serialized LangChain messages, the data is in kwargs\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // Check response_metadata.output (GPT-5 final style) - for values events\n const responseMetadata = (\n kwargs as { response_metadata?: { output?: unknown[] } }\n ).response_metadata;\n if (responseMetadata && Array.isArray(responseMetadata.output)) {\n const reasoningParts: string[] = [];\n for (const item of responseMetadata.output) {\n if (isGPT5ReasoningOutput(item)) {\n // Extract text from summary array - handles both { type: \"summary_text\", text } and { text } formats\n for (const summaryItem of item.summary) {\n if (typeof summaryItem === 'object' && summaryItem !== null) {\n const text = (summaryItem as { text?: string }).text;\n if (typeof text === 'string' && text) {\n reasoningParts.push(text);\n }\n }\n }\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n // Also check additional_kwargs.reasoning.summary as fallback\n const additionalKwargs = (\n kwargs as { additional_kwargs?: { reasoning?: { summary?: unknown[] } } }\n ).additional_kwargs;\n if (\n additionalKwargs?.reasoning &&\n Array.isArray(additionalKwargs.reasoning.summary)\n ) {\n const reasoningParts: string[] = [];\n for (const summaryItem of additionalKwargs.reasoning.summary) {\n if (\n typeof summaryItem === 'object' &&\n summaryItem !== null &&\n 'text' in summaryItem &&\n typeof (summaryItem as { text: unknown }).text === 'string'\n ) {\n reasoningParts.push((summaryItem as { text: string }).text);\n }\n }\n if (reasoningParts.length > 0) {\n return reasoningParts.join('');\n }\n }\n\n return undefined;\n}\n\nexport function isCitationContentBlock(\n obj: unknown,\n): obj is ContentBlock.Citation {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: unknown }).type === 'citation'\n );\n}\n\n/**\n * LangChain Core standardizes citations as `{ type: 'citation', ... }` entries in\n * the `annotations` array of a text content block. The text extractors elsewhere\n * in this file only read the `text` field, so without this the citations would be\n * dropped instead of surfaced as AI SDK source parts.\n *\n * Handles both class instances and serialized LangChain message objects.\n */\nexport function extractCitationsFromContentBlocks(\n msg: unknown,\n): NormalizedCitation[] {\n if (msg == null || typeof msg !== 'object') return [];\n\n const msgObj = msg as Record<string, unknown>;\n const kwargs =\n msgObj.kwargs && typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n // `contentBlocks` is the normalized view derived from `content`; reading both\n // would double-count annotations, so prefer it and only fall back to `content`.\n const contentBlocks = (kwargs as { contentBlocks?: unknown }).contentBlocks;\n const content = (kwargs as { content?: unknown }).content;\n const blockSources: unknown[] = Array.isArray(contentBlocks)\n ? contentBlocks\n : Array.isArray(content)\n ? content\n : [];\n\n const citations: NormalizedCitation[] = [];\n\n for (const block of blockSources) {\n if (block == null || typeof block !== 'object') continue;\n\n const annotations = (block as { annotations?: unknown }).annotations;\n if (!Array.isArray(annotations)) continue;\n\n for (const annotation of annotations) {\n if (!isCitationContentBlock(annotation)) continue;\n\n citations.push({\n url: annotation.url,\n title: annotation.title,\n source: annotation.source,\n citedText: annotation.citedText,\n startIndex: annotation.startIndex,\n endIndex: annotation.endIndex,\n });\n }\n }\n\n return citations;\n}\n\nfunction buildSourceProviderMetadata(\n citation: NormalizedCitation,\n): ProviderMetadata | undefined {\n const langchain: Record<string, JSONValue> = {};\n\n if (typeof citation.citedText === 'string') {\n langchain.citedText = citation.citedText;\n }\n if (typeof citation.startIndex === 'number') {\n langchain.startIndex = citation.startIndex;\n }\n if (typeof citation.endIndex === 'number') {\n langchain.endIndex = citation.endIndex;\n }\n if (typeof citation.source === 'string') {\n langchain.source = citation.source;\n }\n\n if (Object.keys(langchain).length === 0) {\n return undefined;\n }\n\n return { langchain };\n}\n\n/**\n * Emits AI SDK source chunks (`source-url` / `source-document`) for the given\n * citations.\n *\n * Citations with a `url` become `source-url` parts keyed by that url; the rest\n * become `source-document` parts. Citation metadata that the source parts cannot\n * represent natively (`citedText`, `startIndex`, `endIndex`, `source`) is preserved\n * under `providerMetadata.langchain`.\n *\n * `emittedSourceIds` dedupes across the LangGraph messages and values events, which\n * can each surface the same citation. URL-less citations are keyed by their content\n * rather than position, so a differing citation subset/order between those two events\n * does not cause an id collision.\n */\nexport function emitSourceChunks(\n citations: NormalizedCitation[],\n messageId: string,\n emittedSourceIds: Set<string>,\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n for (const citation of citations) {\n if (citation.url) {\n if (emittedSourceIds.has(citation.url)) continue;\n emittedSourceIds.add(citation.url);\n\n const providerMetadata = buildSourceProviderMetadata(citation);\n controller.enqueue({\n type: 'source-url',\n sourceId: citation.url,\n url: citation.url,\n ...(citation.title ? { title: citation.title } : {}),\n ...(providerMetadata ? { providerMetadata } : {}),\n });\n continue;\n }\n\n // A citation with no url and no human-readable label carries no information a\n // UI could render, so skip it rather than emit a placeholder source.\n const title = citation.title ?? citation.source;\n if (!title) continue;\n\n const sourceId = `${messageId}:${title}:${citation.citedText ?? ''}:${citation.startIndex ?? ''}:${citation.endIndex ?? ''}`;\n if (emittedSourceIds.has(sourceId)) continue;\n emittedSourceIds.add(sourceId);\n\n const providerMetadata = buildSourceProviderMetadata(citation);\n controller.enqueue({\n type: 'source-document',\n sourceId,\n mediaType: 'text/plain',\n title,\n ...(providerMetadata ? { providerMetadata } : {}),\n });\n }\n}\n\n/**\n * Checks if an object is an image generation output\n *\n * @param obj - The object to check.\n * @returns True if the object is an image generation output, false otherwise.\n */\nexport function isImageGenerationOutput(\n obj: unknown,\n): obj is ImageGenerationOutput {\n return (\n obj != null &&\n typeof obj === 'object' &&\n 'type' in obj &&\n (obj as { type: string }).type === 'image_generation_call'\n );\n}\n\n/**\n * Extracts image generation outputs from `additional_kwargs`\n *\n * @param additionalKwargs - The additional kwargs to extract the image generation outputs from.\n * @returns The image generation outputs.\n */\nexport function extractImageOutputs(\n additionalKwargs: Record<string, unknown> | undefined,\n): ImageGenerationOutput[] {\n if (!additionalKwargs) return [];\n\n const toolOutputs = additionalKwargs.tool_outputs;\n if (!Array.isArray(toolOutputs)) return [];\n\n return toolOutputs.filter(isImageGenerationOutput);\n}\n\nfunction formatToolError(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n\n try {\n const serialized = JSON.stringify(error);\n return serialized ?? String(error);\n } catch {\n return String(error);\n }\n}\n\n/**\n * Returns per-message bookkeeping, creating it on first use.\n * Map keys keep remote-controlled message IDs like \"__proto__\" from touching Object.prototype.\n */\nfunction getOrCreateMessageSeen(\n messageSeen: LangGraphEventState['messageSeen'],\n msgId: string,\n): LangGraphMessageSeen {\n let seen = messageSeen.get(msgId);\n if (!seen) {\n seen = {};\n messageSeen.set(msgId, seen);\n }\n return seen;\n}\n\n/**\n * Returns the per-index tool call metadata for a message, creating it on first use.\n * Later streamed chunks use this to recover tool call IDs/names from earlier chunks.\n */\nfunction getOrCreateToolCallInfoByIndex(\n toolCallInfoByIndex: LangGraphEventState['toolCallInfoByIndex'],\n msgId: string,\n): Map<number, { id: string; name: string }> {\n let toolCallInfo = toolCallInfoByIndex.get(msgId);\n if (!toolCallInfo) {\n toolCallInfo = new Map();\n toolCallInfoByIndex.set(msgId, toolCallInfo);\n }\n return toolCallInfo;\n}\n\n/**\n * Processes a LangGraph event and emits UI message chunks.\n *\n * @param event - The event to process.\n * @param state - The state of the LangGraph event.\n * @param controller - The controller to use to emit the UI message chunks.\n */\nexport function processLangGraphEvent(\n event: unknown[],\n state: LangGraphEventState,\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n): void {\n const {\n messageSeen,\n messageConcat,\n emittedToolCalls,\n emittedImages,\n emittedReasoningIds,\n messageReasoningIds,\n toolCallInfoByIndex,\n emittedToolCallsByKey,\n emittedToolInputs,\n } = state;\n const [type, data] = parseLangGraphEvent(event);\n\n switch (type) {\n case 'custom': {\n /**\n * Extract custom event type from the data's 'type' field if present.\n * This allows users to emit custom events like:\n * writer({ type: 'progress', value: 50 }) -> { type: 'data-progress', data: {...} }\n * writer({ type: 'status', message: '...' }) -> { type: 'data-status', data: {...} }\n * writer({ key: 'value' }) -> { type: 'data-custom', data: {...} } (fallback)\n *\n * The 'id' field can be used to make parts persistent and updateable.\n * Parts with an 'id' are NOT transient (added to message.parts).\n * Parts without an 'id' are transient (only passed to onData callback).\n */\n let customTypeName = 'custom';\n let partId: string | undefined;\n\n if (data != null && typeof data === 'object' && !Array.isArray(data)) {\n const dataObj = data as Record<string, unknown>;\n if (typeof dataObj.type === 'string' && dataObj.type) {\n customTypeName = dataObj.type;\n }\n if (typeof dataObj.id === 'string' && dataObj.id) {\n partId = dataObj.id;\n }\n }\n\n controller.enqueue({\n type: `data-${customTypeName}` as `data-${string}`,\n id: partId,\n transient: partId == null,\n data,\n });\n break;\n }\n\n case 'messages': {\n const [rawMsg, metadata] = data as [\n BaseMessageChunk | BaseMessage | undefined,\n Record<string, unknown> | undefined,\n ];\n\n const msg = rawMsg;\n const msgId = getMessageId(msg);\n\n if (!msgId) return;\n\n /**\n * Track LangGraph step changes and emit start-step/finish-step events.\n * Before emitting finish-step, close any open text/reasoning parts so\n * the client does not receive orphaned deltas after its\n * activeReasoningParts / activeTextParts have been cleared.\n */\n const langgraphStep =\n typeof metadata?.langgraph_step === 'number'\n ? metadata.langgraph_step\n : null;\n if (langgraphStep !== null && langgraphStep !== state.currentStep) {\n if (state.currentStep !== null) {\n for (const [id, seen] of messageSeen) {\n if (seen.text) {\n controller.enqueue({ type: 'text-end', id });\n }\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n messageSeen.delete(id);\n messageConcat.delete(id);\n messageReasoningIds.delete(id);\n }\n controller.enqueue({ type: 'finish-step' });\n }\n controller.enqueue({ type: 'start-step' });\n state.currentStep = langgraphStep;\n }\n\n /**\n * Accumulate message chunks for later reference\n * Note: Only works for actual class instances, not serialized messages\n */\n if (AIMessageChunk.isInstance(msg)) {\n const existingMessage = messageConcat.get(msgId);\n if (existingMessage) {\n messageConcat.set(\n msgId,\n existingMessage.concat(msg) as AIMessageChunk,\n );\n } else {\n messageConcat.set(msgId, msg);\n }\n }\n\n if (isAIMessageChunk(msg)) {\n const concatChunk = messageConcat.get(msgId);\n\n /**\n * Handle image generation outputs from additional_kwargs.tool_outputs\n * Handle both direct properties and serialized messages (kwargs)\n */\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n const additionalKwargs = dataSource.additional_kwargs as\n | Record<string, unknown>\n | undefined;\n const imageOutputs = extractImageOutputs(additionalKwargs);\n\n for (const imageOutput of imageOutputs) {\n /**\n * Only emit if we have image data and haven't emitted this image yet\n */\n if (imageOutput.result && !emittedImages.has(imageOutput.id)) {\n emittedImages.add(imageOutput.id);\n\n /**\n * Emit as a file part using proper AI SDK multimodal format\n */\n const mediaType = `image/${imageOutput.output_format || 'png'}`;\n controller.enqueue({\n type: 'file',\n mediaType,\n url: `data:${mediaType};base64,${imageOutput.result}`,\n });\n }\n }\n\n /**\n * Handle tool call chunks for streaming tool calls\n * Access from dataSource to handle both direct and serialized messages\n *\n * Tool call chunks are streamed as follows:\n * 1. First chunk: has name, id, but often empty args\n * 2. Subsequent chunks: have args but NO id or name\n *\n * We store tool call info by index when we first see it, then look it up\n * for subsequent chunks that don't include the id.\n */\n const toolCallChunks = dataSource.tool_call_chunks as\n | ToolCallChunk[]\n | undefined;\n if (toolCallChunks?.length) {\n for (const toolCallChunk of toolCallChunks) {\n const toolCallIndex = toolCallChunk.index ?? 0;\n\n /**\n * If this chunk has an id, store it for future lookups by index\n */\n if (toolCallChunk.id) {\n getOrCreateToolCallInfoByIndex(toolCallInfoByIndex, msgId).set(\n toolCallIndex,\n {\n id: toolCallChunk.id,\n name:\n toolCallChunk.name ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.name ||\n 'unknown',\n },\n );\n }\n\n /**\n * Get the tool call ID from the chunk, stored info, or accumulated chunks\n */\n const storedToolCallInfo = toolCallInfoByIndex\n .get(msgId)\n ?.get(toolCallIndex);\n const toolCallId =\n toolCallChunk.id ||\n storedToolCallInfo?.id ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.id;\n\n /**\n * Skip if we don't have a proper tool call ID - we'll handle it in values\n */\n if (!toolCallId) {\n continue;\n }\n\n const toolName =\n toolCallChunk.name ||\n storedToolCallInfo?.name ||\n concatChunk?.tool_call_chunks?.[toolCallIndex]?.name ||\n 'unknown';\n\n /**\n * Emit tool-input-start when we first see this tool call\n * (even if args is empty - the first chunk often has empty args)\n * Set dynamic: true to enable HITL approval requests\n */\n const seen = messageSeen.get(msgId);\n if (!seen?.tool?.has(toolCallId)) {\n const updatedSeen = getOrCreateMessageSeen(messageSeen, msgId);\n updatedSeen.tool ??= new Set();\n updatedSeen.tool.add(toolCallId);\n\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: toolCallId,\n toolName: toolName,\n dynamic: true,\n });\n }\n }\n\n /**\n * Only emit tool-input-delta when args is non-empty\n */\n if (toolCallChunk.args) {\n controller.enqueue({\n type: 'tool-input-delta',\n toolCallId: toolCallId,\n inputTextDelta: toolCallChunk.args,\n });\n }\n }\n\n return;\n }\n\n /**\n * Handle reasoning content from contentBlocks\n * Streaming chunks contain DELTA text (not accumulated), so emit directly.\n * Use reasoning block ID for deduplication as it's consistent across streaming and values events.\n *\n * Important: Early chunks may have reasoning ID but no content, later chunks may\n * have content but no reasoning ID. We capture the ID when first seen and reuse it.\n * We also immediately add to emittedReasoningIds to prevent values events from\n * emitting the same reasoning (values events can arrive between streaming chunks).\n */\n // Capture reasoning ID when we first see it (even if no content yet)\n const chunkReasoningId = extractReasoningId(msg);\n if (chunkReasoningId) {\n if (!messageReasoningIds.has(msgId)) {\n messageReasoningIds.set(msgId, chunkReasoningId);\n }\n // Immediately mark as emitted to prevent values from duplicating\n // This must happen as soon as we see the ID, before content arrives\n emittedReasoningIds.add(chunkReasoningId);\n }\n\n const reasoning = extractReasoningFromContentBlocks(msg);\n if (reasoning) {\n // Use stored reasoning ID, or current chunk's ID, or fall back to message ID\n const reasoningId =\n messageReasoningIds.get(msgId) ?? chunkReasoningId ?? msgId;\n\n const seen = messageSeen.get(msgId);\n if (!seen?.reasoning) {\n controller.enqueue({ type: 'reasoning-start', id: msgId });\n getOrCreateMessageSeen(messageSeen, msgId).reasoning = true;\n }\n\n // Streaming chunks have delta text, emit directly without slicing\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: msgId,\n });\n // Also ensure the reasoning ID is marked (handles case where ID wasn't in first chunk)\n emittedReasoningIds.add(reasoningId);\n }\n\n /**\n * Handle text content\n */\n const text = getMessageText(msg);\n if (text) {\n const seen = messageSeen.get(msgId);\n if (seen?.reasoning && !seen.text) {\n controller.enqueue({ type: 'reasoning-end', id: msgId });\n seen.reasoning = false;\n }\n\n if (!seen?.text) {\n controller.enqueue({ type: 'text-start', id: msgId });\n getOrCreateMessageSeen(messageSeen, msgId).text = true;\n }\n\n controller.enqueue({\n type: 'text-delta',\n delta: text,\n id: msgId,\n });\n }\n\n const citations = extractCitationsFromContentBlocks(msg);\n if (citations.length > 0) {\n emitSourceChunks(\n citations,\n msgId,\n state.emittedSourceIds,\n controller,\n );\n }\n } else if (isToolMessageType(msg)) {\n // Handle both direct properties and serialized messages (kwargs)\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n const toolCallId = dataSource.tool_call_id as string | undefined;\n const status = dataSource.status as string | undefined;\n\n if (toolCallId) {\n if (status === 'error') {\n // Tool execution failed\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId,\n errorText:\n typeof dataSource.content === 'string'\n ? dataSource.content\n : 'Tool execution failed',\n });\n } else {\n // Tool execution succeeded\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: dataSource.content,\n });\n }\n }\n }\n\n return;\n }\n\n case 'tools': {\n if (data == null || typeof data !== 'object' || Array.isArray(data)) {\n return;\n }\n\n const payload = data as {\n event?: unknown;\n name?: unknown;\n input?: unknown;\n data?: unknown;\n output?: unknown;\n error?: unknown;\n toolCallId?: unknown;\n };\n const toolCallId =\n typeof payload.toolCallId === 'string' ? payload.toolCallId : undefined;\n const toolName =\n typeof payload.name === 'string' ? payload.name : 'unknown';\n\n if (!toolCallId) return;\n\n const ensureToolInputLifecycle = () => {\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId,\n toolName,\n dynamic: true,\n });\n }\n\n if (!emittedToolInputs.has(toolCallId)) {\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input: payload.input,\n dynamic: true,\n });\n }\n };\n\n switch (payload.event) {\n case 'on_tool_start': {\n const toolCallKey = `${toolName}:${JSON.stringify(payload.input)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n\n ensureToolInputLifecycle();\n break;\n }\n\n case 'on_tool_event': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: payload.data,\n preliminary: true,\n });\n break;\n }\n\n case 'on_tool_end': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId,\n output: payload.output,\n });\n break;\n }\n\n case 'on_tool_error': {\n ensureToolInputLifecycle();\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId,\n errorText: formatToolError(payload.error),\n });\n break;\n }\n }\n\n return;\n }\n\n case 'values': {\n /**\n * Finalize all pending message chunks\n */\n for (const [id, seen] of messageSeen) {\n if (seen.text) controller.enqueue({ type: 'text-end', id });\n if (seen.tool) {\n for (const toolCallId of seen.tool) {\n const concatMsg = messageConcat.get(id);\n const toolCall = concatMsg?.tool_calls?.find(\n call => call.id === toolCallId,\n );\n\n if (toolCall) {\n emittedToolCalls.add(toolCallId);\n // Store mapping for HITL interrupt lookup\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n if (!emittedToolInputs.has(toolCallId)) {\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName: toolCall.name,\n input: toolCall.args,\n dynamic: true,\n });\n }\n }\n }\n }\n\n if (seen.reasoning) {\n controller.enqueue({ type: 'reasoning-end', id });\n }\n\n messageSeen.delete(id);\n messageConcat.delete(id);\n messageReasoningIds.delete(id);\n }\n\n /**\n * Also check for tool calls in the final state that weren't streamed\n * This handles cases where tool calls appear directly in values without being in messages events\n */\n if (data != null && typeof data === 'object' && 'messages' in data) {\n const messages = (data as { messages?: unknown[] }).messages;\n if (Array.isArray(messages)) {\n /**\n * First pass: Collect all tool call IDs that have been responded to by ToolMessages.\n * These are historical tool calls that are already complete.\n */\n const completedToolCallIds = new Set<string>();\n for (const msg of messages) {\n if (!msg || typeof msg !== 'object') continue;\n\n if (isToolMessageType(msg)) {\n // Handle both direct properties and serialized messages (kwargs)\n const msgObj = msg as unknown as Record<string, unknown>;\n const dataSource =\n msgObj.type === 'constructor' &&\n msgObj.kwargs &&\n typeof msgObj.kwargs === 'object'\n ? (msgObj.kwargs as Record<string, unknown>)\n : msgObj;\n\n const toolCallId = dataSource.tool_call_id as string | undefined;\n if (toolCallId) {\n completedToolCallIds.add(toolCallId);\n }\n }\n }\n\n /**\n * Second pass: Process messages and emit tool events only for NEW tool calls\n * (those not already completed by a ToolMessage in the history)\n */\n for (const msg of messages) {\n if (!msg || typeof msg !== 'object') continue;\n\n // Use getMessageId to handle both class instances and serialized messages\n const msgId = getMessageId(msg);\n if (!msgId) continue;\n\n /**\n * Check if this is an AI message with tool calls\n */\n let toolCalls: ToolCall[] | undefined;\n\n /**\n * For class instances\n */\n if (AIMessageChunk.isInstance(msg) || AIMessage.isInstance(msg)) {\n toolCalls = msg.tool_calls;\n } else if (isPlainMessageObject(msg)) {\n /**\n * For plain objects from RemoteGraph API or serialized LangChain messages\n */\n const messageRecord = msg as Record<string, unknown>;\n\n /**\n * Determine the data source (handle both direct and serialized formats)\n */\n const isSerializedFormat =\n messageRecord.type === 'constructor' &&\n Array.isArray(messageRecord.id) &&\n ((messageRecord.id as string[]).includes('AIMessageChunk') ||\n (messageRecord.id as string[]).includes('AIMessage'));\n const dataSource = isSerializedFormat\n ? (messageRecord.kwargs as Record<string, unknown>)\n : messageRecord;\n\n if (\n messageRecord.type === 'ai' ||\n messageRecord.type === 'AIMessageChunk' ||\n isSerializedFormat\n ) {\n /**\n * Try tool_calls first (normalized format)\n */\n if (Array.isArray(dataSource?.tool_calls)) {\n toolCalls = dataSource.tool_calls as ToolCall[];\n } else if (\n /**\n * Fall back to additional_kwargs.tool_calls (OpenAI format)\n */\n dataSource?.additional_kwargs &&\n typeof dataSource.additional_kwargs === 'object'\n ) {\n const additionalKwargs =\n dataSource.additional_kwargs as Record<string, unknown>;\n if (Array.isArray(additionalKwargs.tool_calls)) {\n /**\n * Convert OpenAI format to normalized format\n */\n toolCalls = (\n additionalKwargs.tool_calls as Array<{\n id?: string;\n function?: { name?: string; arguments?: string };\n }>\n ).map((tc, idx) => {\n const functionData = tc.function;\n let args: unknown;\n try {\n args = functionData?.arguments\n ? JSON.parse(functionData.arguments)\n : {};\n } catch {\n args = {};\n }\n return {\n id: tc.id || `call_${idx}`,\n name: functionData?.name || 'unknown',\n args,\n } as ToolCall;\n });\n }\n }\n }\n }\n\n if (toolCalls && toolCalls.length > 0) {\n for (const toolCall of toolCalls) {\n /**\n * Only emit if we haven't already processed this tool call\n * AND if it's not a historical tool call that already has a ToolMessage response.\n * Historical completed tool calls should not be re-emitted as this would create\n * orphaned tool parts in the UI without corresponding outputs.\n */\n if (\n toolCall.id &&\n !emittedToolCalls.has(toolCall.id) &&\n !completedToolCallIds.has(toolCall.id)\n ) {\n emittedToolCalls.add(toolCall.id);\n // Store mapping for HITL interrupt lookup\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCall.id);\n /**\n * Emit tool-input-start first to ensure proper lifecycle.\n * Tool calls that weren't streamed (no tool_call_chunks) need\n * the start event before tool-input-available.\n */\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n dynamic: true,\n });\n emittedToolInputs.add(toolCall.id);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n input: toolCall.args,\n dynamic: true,\n });\n } else if (toolCall.id && emittedToolCalls.has(toolCall.id)) {\n // Register key mapping for tool calls already emitted via messages mode\n // so that __interrupt__ handling can match them by key\n const toolCallKey = `${toolCall.name}:${JSON.stringify(toolCall.args)}`;\n emittedToolCallsByKey.set(toolCallKey, toolCall.id);\n }\n }\n }\n\n /**\n * Check for reasoning content that wasn't streamed\n * Use reasoning block ID for deduplication as it's consistent across streaming and values.\n *\n * IMPORTANT: Handle two cases differently:\n * 1. Message has reasoning WITHOUT tool_calls → emit reasoning (pure reasoning case)\n * 2. Message has reasoning WITH tool_calls → only emit if streamed this request\n * (When resuming from HITL interrupt, historical messages have both reasoning\n * AND tool_calls. We skip those to avoid duplicate reasoning entries.)\n */\n const reasoningId = extractReasoningId(msg);\n const wasStreamedThisRequest = messageSeen.has(msgId);\n const hasToolCalls = toolCalls && toolCalls.length > 0;\n\n /**\n * Determine if we should emit reasoning:\n * - If we already emitted this reasoning ID, skip\n * - If the message was streamed this request, emit (normal flow)\n * - If NOT streamed but has NO tool_calls, emit (pure reasoning in values case)\n * - If NOT streamed but HAS tool_calls, skip (historical HITL message)\n */\n const shouldEmitReasoning =\n reasoningId &&\n !emittedReasoningIds.has(reasoningId) &&\n (wasStreamedThisRequest || !hasToolCalls);\n\n if (shouldEmitReasoning) {\n /**\n * Use extractReasoningFromValuesMessage which extracts from response_metadata.output\n * This is the full accumulated reasoning, not deltas\n */\n const reasoning = extractReasoningFromValuesMessage(msg);\n\n if (reasoning) {\n controller.enqueue({ type: 'reasoning-start', id: msgId });\n controller.enqueue({\n type: 'reasoning-delta',\n delta: reasoning,\n id: msgId,\n });\n controller.enqueue({ type: 'reasoning-end', id: msgId });\n emittedReasoningIds.add(reasoningId);\n }\n }\n\n const valuesCitations = extractCitationsFromContentBlocks(msg);\n if (valuesCitations.length > 0) {\n emitSourceChunks(\n valuesCitations,\n msgId,\n state.emittedSourceIds,\n controller,\n );\n }\n }\n }\n }\n\n /**\n * Handle Human-in-the-Loop interrupts\n * When HITL middleware pauses execution, the interrupt data is in __interrupt__\n * Note: This is outside the 'messages' check because interrupt can come as a separate event\n */\n if (data != null && typeof data === 'object') {\n const interrupt = (data as Record<string, unknown>).__interrupt__;\n if (Array.isArray(interrupt) && interrupt.length > 0) {\n for (const interruptItem of interrupt) {\n const interruptValue = (interruptItem as { value?: unknown })\n ?.value as Record<string, unknown> | undefined;\n\n if (!interruptValue) continue;\n\n /**\n * Support both camelCase (JS SDK) and snake_case (Python SDK)\n */\n const actionRequests = (interruptValue.actionRequests ||\n interruptValue.action_requests) as\n | Array<{\n name: string;\n args?: Record<string, unknown>; // JS SDK uses 'args'\n arguments?: Record<string, unknown>; // Python SDK uses 'arguments'\n id?: string;\n }>\n | undefined;\n\n if (!Array.isArray(actionRequests)) continue;\n\n for (const actionRequest of actionRequests) {\n const toolName = actionRequest.name;\n /**\n * Support both 'args' (JS SDK) and 'arguments' (Python SDK)\n */\n const input = actionRequest.args || actionRequest.arguments;\n\n /**\n * Look up the original tool call ID using the name+args key\n * Fall back to action request ID or generate one if not found\n */\n const toolCallKey = `${toolName}:${JSON.stringify(input)}`;\n const toolCallId =\n emittedToolCallsByKey.get(toolCallKey) ||\n actionRequest.id ||\n `hitl-${toolName}-${Date.now()}`;\n\n /**\n * First emit tool-input-start then tool-input-available\n * so the UI knows what tool is being called with proper lifecycle\n */\n if (!emittedToolCalls.has(toolCallId)) {\n emittedToolCalls.add(toolCallId);\n emittedToolCallsByKey.set(toolCallKey, toolCallId);\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId,\n toolName,\n dynamic: true,\n });\n emittedToolInputs.add(toolCallId);\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input,\n dynamic: true,\n });\n }\n\n /**\n * Then emit tool-approval-request to mark it as awaiting approval\n */\n controller.enqueue({\n type: 'tool-approval-request',\n approvalId: toolCallId,\n toolCallId,\n });\n }\n }\n }\n }\n\n break;\n }\n }\n}\n","import type { AIMessageChunk } from '@langchain/core/messages';\nimport type {\n UIMessage,\n UIMessageChunk,\n ChatTransport,\n ChatRequestOptions,\n} from 'ai';\nimport {\n RemoteGraph,\n type RemoteGraphParams,\n} from '@langchain/langgraph/remote';\nimport { toBaseMessages, toUIMessageStream } from './adapter';\n\n/**\n * Options for configuring a LangSmith deployment transport.\n * Extends RemoteGraphParams but makes graphId optional (defaults to 'agent').\n */\nexport type LangSmithDeploymentTransportOptions = Omit<\n RemoteGraphParams,\n 'graphId'\n> & {\n /**\n * The ID of the graph to connect to.\n * @default 'agent'\n */\n graphId?: string;\n};\n\n/**\n * A ChatTransport implementation for LangSmith/LangGraph deployments.\n *\n * This transport enables seamless integration between the AI SDK's useChat hook\n * and LangSmith deployed LangGraph agents.\n *\n * @example\n * ```ts\n * import { LangSmithDeploymentTransport } from '@ai-sdk/langchain';\n *\n * // Use with useChat\n * const { messages, input, handleSubmit } = useChat({\n * transport: new LangSmithDeploymentTransport({\n * url: 'https://your-deployment.us.langgraph.app',\n * apiKey: 'my-api-key',\n * }),\n * });\n * ```\n */\nexport class LangSmithDeploymentTransport<\n UI_MESSAGE extends UIMessage,\n> implements ChatTransport<UI_MESSAGE> {\n protected graph: RemoteGraph;\n\n constructor(options: LangSmithDeploymentTransportOptions) {\n this.graph = new RemoteGraph({\n ...options,\n graphId: options.graphId ?? 'agent',\n });\n }\n\n async sendMessages(\n options: {\n trigger: 'submit-message' | 'regenerate-message';\n chatId: string;\n messageId: string | undefined;\n messages: UI_MESSAGE[];\n abortSignal: AbortSignal | undefined;\n } & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk>> {\n const baseMessages = await toBaseMessages(options.messages);\n\n const stream = await this.graph.stream(\n { messages: baseMessages },\n { streamMode: ['values', 'messages'] },\n );\n\n return toUIMessageStream(\n stream as AsyncIterable<AIMessageChunk> | ReadableStream,\n );\n }\n\n async reconnectToStream(\n _options: {\n chatId: string;\n } & ChatRequestOptions,\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n throw new Error('Method not implemented.');\n }\n}\n"],"mappings":"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","names":["providerMetadata"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/langchain",
3
- "version": "3.0.30",
3
+ "version": "3.0.32",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -25,7 +25,7 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "ai": "7.0.30"
28
+ "ai": "7.0.32"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@langchain/core": "^1.1.46",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "22.19.19",
34
34
  "tsup": "^8.5.1",
35
35
  "typescript": "5.8.3",
36
- "@ai-sdk/provider-utils": "5.0.10",
36
+ "@ai-sdk/provider-utils": "5.0.11",
37
37
  "@vercel/ai-tsconfig": "0.0.0"
38
38
  },
39
39
  "peerDependencies": {
package/src/adapter.ts CHANGED
@@ -24,6 +24,27 @@ import {
24
24
  import type { LangGraphEventState } from './types';
25
25
  import type { StreamCallbacks } from './stream-callbacks';
26
26
 
27
+ /**
28
+ * Options for converting a LangChain stream to an AI SDK UIMessageStream.
29
+ */
30
+ export interface ToUIMessageStreamOptions<
31
+ TState = unknown,
32
+ > extends StreamCallbacks<TState> {
33
+ /**
34
+ * Whether to emit the outer `start` chunk.
35
+ *
36
+ * @default true
37
+ */
38
+ sendStart?: boolean;
39
+
40
+ /**
41
+ * Whether to emit the outer `finish` chunk.
42
+ *
43
+ * @default true
44
+ */
45
+ sendFinish?: boolean;
46
+ }
47
+
27
48
  /**
28
49
  * Converts AI SDK UIMessages to LangChain BaseMessage objects.
29
50
  *
@@ -352,7 +373,7 @@ function isAbortError(error: unknown): boolean {
352
373
  * - streamEvents streams (from `agent.streamEvents()` or `model.streamEvents()`)
353
374
  *
354
375
  * @param stream - A stream from LangChain model.stream(), graph.stream(), or streamEvents().
355
- * @param callbacks - Optional callbacks for stream lifecycle events.
376
+ * @param options - Optional lifecycle callbacks and outer chunk controls.
356
377
  * @returns A ReadableStream of UIMessageChunk objects.
357
378
  *
358
379
  * @example
@@ -402,8 +423,10 @@ function isAbortError(error: unknown): boolean {
402
423
  */
403
424
  export function toUIMessageStream<TState = unknown>(
404
425
  stream: AsyncIterable<AIMessageChunk> | ReadableStream,
405
- callbacks?: StreamCallbacks<TState>,
426
+ options?: ToUIMessageStreamOptions<TState>,
406
427
  ): ReadableStream<UIMessageChunk> {
428
+ const { sendStart = true, sendFinish = true } = options ?? {};
429
+
407
430
  /**
408
431
  * Track text chunks for onFinal callback
409
432
  */
@@ -486,10 +509,12 @@ export function toUIMessageStream<TState = unknown>(
486
509
  /**
487
510
  * Intercept text-delta chunks for callbacks
488
511
  */
489
- if (callbacks && chunk.type === 'text-delta' && chunk.delta) {
490
- textChunks.push(chunk.delta);
491
- callbacks.onToken?.(chunk.delta);
492
- callbacks.onText?.(chunk.delta);
512
+ if (chunk.type === 'text-delta' && chunk.delta) {
513
+ if (options?.onFinal) {
514
+ textChunks.push(chunk.delta);
515
+ }
516
+ options?.onToken?.(chunk.delta);
517
+ options?.onText?.(chunk.delta);
493
518
  }
494
519
  originalController.enqueue(chunk);
495
520
  },
@@ -498,10 +523,12 @@ export function toUIMessageStream<TState = unknown>(
498
523
 
499
524
  return new ReadableStream<UIMessageChunk>({
500
525
  async start(controller) {
501
- await callbacks?.onStart?.();
526
+ await options?.onStart?.();
502
527
 
503
528
  const wrappedController = createCallbackController(controller);
504
- controller.enqueue({ type: 'start' });
529
+ if (sendStart) {
530
+ controller.enqueue({ type: 'start' });
531
+ }
505
532
 
506
533
  try {
507
534
  while (true) {
@@ -576,7 +603,9 @@ export function toUIMessageStream<TState = unknown>(
576
603
  id: modelState.textMessageId ?? modelState.messageId,
577
604
  });
578
605
  }
579
- controller.enqueue({ type: 'finish' });
606
+ if (sendFinish) {
607
+ controller.enqueue({ type: 'finish' });
608
+ }
580
609
  } else if (streamType === 'langgraph') {
581
610
  /**
582
611
  * Close any open text/reasoning parts before finishing.
@@ -598,24 +627,26 @@ export function toUIMessageStream<TState = unknown>(
598
627
  if (langGraphState.currentStep !== null) {
599
628
  controller.enqueue({ type: 'finish-step' });
600
629
  }
601
- controller.enqueue({ type: 'finish' });
630
+ if (sendFinish) {
631
+ controller.enqueue({ type: 'finish' });
632
+ }
602
633
  }
603
634
 
604
635
  /**
605
636
  * Call onFinal callback with aggregated text
606
637
  */
607
- await callbacks?.onFinal?.(textChunks.join(''));
608
- await callbacks?.onFinish?.(lastValuesData);
638
+ await options?.onFinal?.(textChunks.join(''));
639
+ await options?.onFinish?.(lastValuesData);
609
640
  } catch (error) {
610
641
  const errorObj =
611
642
  error instanceof Error ? error : new Error(String(error));
612
643
 
613
- await callbacks?.onFinal?.(textChunks.join(''));
644
+ await options?.onFinal?.(textChunks.join(''));
614
645
 
615
646
  if (isAbortError(error)) {
616
- await callbacks?.onAbort?.();
647
+ await options?.onAbort?.();
617
648
  } else {
618
- await callbacks?.onError?.(errorObj);
649
+ await options?.onError?.(errorObj);
619
650
  }
620
651
 
621
652
  controller.enqueue({
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export {
2
2
  toBaseMessages,
3
3
  toUIMessageStream,
4
4
  convertModelMessages,
5
+ type ToUIMessageStreamOptions,
5
6
  } from './adapter';
6
7
 
7
8
  export {
package/src/utils.ts CHANGED
@@ -1505,6 +1505,11 @@ export function processLangGraphEvent(
1505
1505
  const text = getMessageText(msg);
1506
1506
  if (text) {
1507
1507
  const seen = messageSeen.get(msgId);
1508
+ if (seen?.reasoning && !seen.text) {
1509
+ controller.enqueue({ type: 'reasoning-end', id: msgId });
1510
+ seen.reasoning = false;
1511
+ }
1512
+
1508
1513
  if (!seen?.text) {
1509
1514
  controller.enqueue({ type: 'text-start', id: msgId });
1510
1515
  getOrCreateMessageSeen(messageSeen, msgId).text = true;