返回 presentation-ai
@ai-sdk__langchain@2.0.122.patch
根目录 / patches / @ai-sdk__langchain@2.0.122.patch
1 diff --git a/dist/index.d.mts b/dist/index.d.mts
2 index d067e49a..d0dbf5b2 100644
3 --- a/dist/index.d.mts
4 +++ b/dist/index.d.mts
5 @@ -110,6 +110,31 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
6 * ```
7 */
8 declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks<TState>): ReadableStream<UIMessageChunk>;
9 +declare function baseMessagesToUIMessages(messages: BaseMessage[]): UIMessage[];
10 +interface InterruptActionRequest {
11 + name: string;
12 + args?: Record<string, unknown>;
13 + arguments?: Record<string, unknown>;
14 + id?: string;
15 +}
16 +interface InterruptValue {
17 + actionRequests?: InterruptActionRequest[];
18 + action_requests?: InterruptActionRequest[];
19 +}
20 +interface PregelInterrupt {
21 + value?: InterruptValue | unknown;
22 +}
23 +interface PregelTask {
24 + interrupts?: PregelInterrupt[];
25 + [key: string]: unknown;
26 +}
27 +interface StateSnapshotLike {
28 + values: {
29 + messages?: BaseMessage[];
30 + } & Record<string, unknown>;
31 + tasks?: PregelTask[];
32 +}
33 +declare function stateSnapshotToUIMessages(snapshot: StateSnapshotLike): UIMessage[];
34
35 /**
36 * Options for configuring a LangSmith deployment transport.
37 @@ -156,4 +181,4 @@ declare class LangSmithDeploymentTransport<UI_MESSAGE extends UIMessage> impleme
38 } & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
39 }
40
41 -export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StreamCallbacks, convertModelMessages, toBaseMessages, toUIMessageStream };
42 +export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StateSnapshotLike, type StreamCallbacks, baseMessagesToUIMessages, convertModelMessages, stateSnapshotToUIMessages, toBaseMessages, toUIMessageStream };
43 diff --git a/dist/index.d.ts b/dist/index.d.ts
44 index d067e49a..d0dbf5b2 100644
45 --- a/dist/index.d.ts
46 +++ b/dist/index.d.ts
47 @@ -110,6 +110,31 @@ declare function convertModelMessages(modelMessages: ModelMessage[]): BaseMessag
48 * ```
49 */
50 declare function toUIMessageStream<TState = unknown>(stream: AsyncIterable<AIMessageChunk> | ReadableStream, callbacks?: StreamCallbacks<TState>): ReadableStream<UIMessageChunk>;
51 +declare function baseMessagesToUIMessages(messages: BaseMessage[]): UIMessage[];
52 +interface InterruptActionRequest {
53 + name: string;
54 + args?: Record<string, unknown>;
55 + arguments?: Record<string, unknown>;
56 + id?: string;
57 +}
58 +interface InterruptValue {
59 + actionRequests?: InterruptActionRequest[];
60 + action_requests?: InterruptActionRequest[];
61 +}
62 +interface PregelInterrupt {
63 + value?: InterruptValue | unknown;
64 +}
65 +interface PregelTask {
66 + interrupts?: PregelInterrupt[];
67 + [key: string]: unknown;
68 +}
69 +interface StateSnapshotLike {
70 + values: {
71 + messages?: BaseMessage[];
72 + } & Record<string, unknown>;
73 + tasks?: PregelTask[];
74 +}
75 +declare function stateSnapshotToUIMessages(snapshot: StateSnapshotLike): UIMessage[];
76
77 /**
78 * Options for configuring a LangSmith deployment transport.
79 @@ -156,4 +181,4 @@ declare class LangSmithDeploymentTransport<UI_MESSAGE extends UIMessage> impleme
80 } & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
81 }
82
83 -export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StreamCallbacks, convertModelMessages, toBaseMessages, toUIMessageStream };
84 +export { LangSmithDeploymentTransport, type LangSmithDeploymentTransportOptions, type StateSnapshotLike, type StreamCallbacks, baseMessagesToUIMessages, convertModelMessages, stateSnapshotToUIMessages, toBaseMessages, toUIMessageStream };
85 diff --git a/dist/index.js b/dist/index.js
86 index 1430e7fb..02350d54 100644
87 --- a/dist/index.js
88 +++ b/dist/index.js
89 @@ -21,9 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
90 var src_exports = {};
91 __export(src_exports, {
92 LangSmithDeploymentTransport: () => LangSmithDeploymentTransport,
93 + baseMessagesToUIMessages: () => baseMessagesToUIMessages,
94 convertModelMessages: () => convertModelMessages,
95 + stateSnapshotToUIMessages: () => stateSnapshotToUIMessages,
96 toBaseMessages: () => toBaseMessages,
97 - toUIMessageStream: () => toUIMessageStream
98 + toUIMessageStream: () => toUIMessageStream
99 });
100 module.exports = __toCommonJS(src_exports);
101
102 @@ -1071,6 +1073,236 @@ function toUIMessageStream(stream, callbacks) {
103 }
104 });
105 }
106 +function getMessageType(msg) {
107 + if (msg == null || typeof msg !== "object") {
108 + return void 0;
109 + }
110 + const msgObj = msg;
111 + if (typeof msgObj._getType === "function") {
112 + return msgObj._getType();
113 + }
114 + if (typeof msgObj.type === "string" && msgObj.type !== "constructor") {
115 + return msgObj.type;
116 + }
117 + if (msgObj.type === "constructor" && Array.isArray(msgObj.id)) {
118 + const ids = msgObj.id;
119 + if (ids.includes("HumanMessage") || ids.includes("HumanMessageChunk")) {
120 + return "human";
121 + }
122 + if (ids.includes("AIMessage") || ids.includes("AIMessageChunk")) {
123 + return "ai";
124 + }
125 + if (ids.includes("SystemMessage") || ids.includes("SystemMessageChunk")) {
126 + return "system";
127 + }
128 + if (ids.includes("ToolMessage") || ids.includes("ToolMessageChunk")) {
129 + return "tool";
130 + }
131 + }
132 + return void 0;
133 +}
134 +function getToolCallId(msg) {
135 + if (msg == null || typeof msg !== "object") {
136 + return void 0;
137 + }
138 + const msgObj = msg;
139 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
140 + return typeof dataSource.tool_call_id === "string" ? dataSource.tool_call_id : void 0;
141 +}
142 +function getToolCalls(msg) {
143 + if (msg == null || typeof msg !== "object") {
144 + return [];
145 + }
146 + const msgObj = msg;
147 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
148 + if (Array.isArray(dataSource.tool_calls)) {
149 + return dataSource.tool_calls;
150 + }
151 + if (dataSource.additional_kwargs && typeof dataSource.additional_kwargs === "object") {
152 + const additionalKwargs = dataSource.additional_kwargs;
153 + if (Array.isArray(additionalKwargs.tool_calls)) {
154 + return additionalKwargs.tool_calls.map((toolCall, idx) => {
155 + let args;
156 + try {
157 + args = toolCall.function && toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
158 + } catch {
159 + args = {};
160 + }
161 + return {
162 + id: toolCall.id ?? `call_${idx}`,
163 + name: toolCall.function && toolCall.function.name ? toolCall.function.name : "unknown",
164 + args
165 + };
166 + });
167 + }
168 + }
169 + return [];
170 +}
171 +function getAdditionalKwargs(msg) {
172 + if (msg == null || typeof msg !== "object") {
173 + return void 0;
174 + }
175 + const msgObj = msg;
176 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
177 + return dataSource.additional_kwargs;
178 +}
179 +function baseMessagesToUIMessages(messages) {
180 + const result = [];
181 + let currentAssistant = null;
182 + for (const msg of messages) {
183 + const msgType = getMessageType(msg);
184 + const msgId = getMessageId(msg) ?? (0, import_ai.generateId)();
185 + switch (msgType) {
186 + case "human": {
187 + currentAssistant = null;
188 + const text = getMessageText(msg);
189 + result.push({
190 + id: msgId,
191 + role: "user",
192 + parts: [{ type: "text", text }]
193 + });
194 + break;
195 + }
196 + case "system": {
197 + currentAssistant = null;
198 + const text = getMessageText(msg);
199 + result.push({
200 + id: msgId,
201 + role: "system",
202 + parts: [{ type: "text", text }]
203 + });
204 + break;
205 + }
206 + case "ai": {
207 + const parts = [];
208 + const reasoning = extractReasoningFromContentBlocks(msg) || extractReasoningFromValuesMessage(msg);
209 + if (reasoning) {
210 + parts.push({ type: "reasoning", text: reasoning, state: "done" });
211 + }
212 + const text = getMessageText(msg);
213 + if (text) {
214 + parts.push({ type: "text", text });
215 + }
216 + const additionalKwargs = getAdditionalKwargs(msg);
217 + const imageOutputs = extractImageOutputs(additionalKwargs);
218 + for (const imageOutput of imageOutputs) {
219 + if (imageOutput.result) {
220 + const mediaType = `image/${imageOutput.output_format ?? "png"}`;
221 + parts.push({
222 + type: "file",
223 + mediaType,
224 + url: `data:${mediaType};base64,${imageOutput.result}`
225 + });
226 + }
227 + }
228 + const toolCalls = getToolCalls(msg);
229 + for (const toolCall of toolCalls) {
230 + parts.push({
231 + type: "dynamic-tool",
232 + toolCallId: toolCall.id,
233 + toolName: toolCall.name,
234 + state: "input-available",
235 + input: toolCall.args
236 + });
237 + }
238 + const assistantMessage = {
239 + id: msgId,
240 + role: "assistant",
241 + parts
242 + };
243 + result.push(assistantMessage);
244 + currentAssistant = assistantMessage;
245 + break;
246 + }
247 + case "tool": {
248 + const toolCallId = getToolCallId(msg);
249 + if (toolCallId && currentAssistant) {
250 + const toolPart = currentAssistant.parts.find((part) => part.type === "dynamic-tool" && part.toolCallId === toolCallId);
251 + if (toolPart) {
252 + const idx = currentAssistant.parts.indexOf(toolPart);
253 + const content = getMessageText(msg);
254 + currentAssistant.parts[idx] = {
255 + type: "dynamic-tool",
256 + toolCallId: toolPart.toolCallId,
257 + toolName: toolPart.toolName,
258 + state: "output-available",
259 + input: toolPart.input,
260 + output: content
261 + };
262 + }
263 + }
264 + break;
265 + }
266 + }
267 + }
268 + return result;
269 +}
270 +function extractInterruptParts(tasks) {
271 + if (!Array.isArray(tasks)) {
272 + return [];
273 + }
274 + const parts = [];
275 + for (const task of tasks) {
276 + if (!Array.isArray(task.interrupts)) {
277 + continue;
278 + }
279 + for (const interrupt of task.interrupts) {
280 + if (!interrupt.value || typeof interrupt.value !== "object") {
281 + continue;
282 + }
283 + const interruptValue = interrupt.value;
284 + const actionRequests = interruptValue.actionRequests ?? interruptValue.action_requests;
285 + if (!Array.isArray(actionRequests)) {
286 + continue;
287 + }
288 + for (const request of actionRequests) {
289 + parts.push({
290 + type: "dynamic-tool",
291 + toolCallId: request.id ?? (0, import_ai.generateId)(),
292 + toolName: request.name,
293 + state: "input-available",
294 + input: request.args ?? request.arguments ?? {}
295 + });
296 + }
297 + }
298 + }
299 + return parts;
300 +}
301 +function stateSnapshotToUIMessages(snapshot) {
302 + var _a;
303 + const messages = (_a = snapshot.values) == null ? void 0 : _a.messages;
304 + if (!Array.isArray(messages)) {
305 + return [];
306 + }
307 + const uiMessages = baseMessagesToUIMessages(messages);
308 + const interruptParts = extractInterruptParts(snapshot.tasks);
309 + if (interruptParts.length > 0) {
310 + let lastAssistant = uiMessages.findLast((message) => message.role === "assistant");
311 + if (!lastAssistant) {
312 + lastAssistant = {
313 + id: (0, import_ai.generateId)(),
314 + role: "assistant",
315 + parts: []
316 + };
317 + uiMessages.push(lastAssistant);
318 + }
319 + const existingToolCallIds = new Set();
320 + const existingToolParts = new Set();
321 + for (const part of lastAssistant.parts) {
322 + if (part.type === "dynamic-tool") {
323 + existingToolCallIds.add(part.toolCallId);
324 + existingToolParts.add(`${part.toolName}:${JSON.stringify(part.input)}`);
325 + }
326 + }
327 + for (const interruptPart of interruptParts) {
328 + const toolCallKey = `${interruptPart.toolName}:${JSON.stringify(interruptPart.input)}`;
329 + if (!existingToolCallIds.has(interruptPart.toolCallId) && !existingToolParts.has(toolCallKey)) {
330 + lastAssistant.parts.push(interruptPart);
331 + }
332 + }
333 + }
334 + return uiMessages;
335 +}
336
337 // src/transport.ts
338 var import_remote = require("@langchain/langgraph/remote");
339 @@ -1099,8 +1331,10 @@ var LangSmithDeploymentTransport = class {
340 // Annotate the CommonJS export names for ESM import in node:
341 0 && (module.exports = {
342 LangSmithDeploymentTransport,
343 + baseMessagesToUIMessages,
344 convertModelMessages,
345 + stateSnapshotToUIMessages,
346 toBaseMessages,
347 - toUIMessageStream
348 + toUIMessageStream
349 });
350 //# sourceMappingURL=index.js.map
351 \ No newline at end of file
352 diff --git a/dist/index.mjs b/dist/index.mjs
353 index 94f5fa8b..751e20d2 100644
354 --- a/dist/index.mjs
355 +++ b/dist/index.mjs
356 @@ -3,7 +3,8 @@ import {
357 SystemMessage
358 } from "@langchain/core/messages";
359 import {
360 - convertToModelMessages
361 + convertToModelMessages,
362 + generateId
363 } from "ai";
364
365 // src/utils.ts
366 @@ -1051,6 +1052,236 @@ function toUIMessageStream(stream, callbacks) {
367 }
368 });
369 }
370 +function getMessageType(msg) {
371 + if (msg == null || typeof msg !== "object") {
372 + return void 0;
373 + }
374 + const msgObj = msg;
375 + if (typeof msgObj._getType === "function") {
376 + return msgObj._getType();
377 + }
378 + if (typeof msgObj.type === "string" && msgObj.type !== "constructor") {
379 + return msgObj.type;
380 + }
381 + if (msgObj.type === "constructor" && Array.isArray(msgObj.id)) {
382 + const ids = msgObj.id;
383 + if (ids.includes("HumanMessage") || ids.includes("HumanMessageChunk")) {
384 + return "human";
385 + }
386 + if (ids.includes("AIMessage") || ids.includes("AIMessageChunk")) {
387 + return "ai";
388 + }
389 + if (ids.includes("SystemMessage") || ids.includes("SystemMessageChunk")) {
390 + return "system";
391 + }
392 + if (ids.includes("ToolMessage") || ids.includes("ToolMessageChunk")) {
393 + return "tool";
394 + }
395 + }
396 + return void 0;
397 +}
398 +function getToolCallId(msg) {
399 + if (msg == null || typeof msg !== "object") {
400 + return void 0;
401 + }
402 + const msgObj = msg;
403 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
404 + return typeof dataSource.tool_call_id === "string" ? dataSource.tool_call_id : void 0;
405 +}
406 +function getToolCalls(msg) {
407 + if (msg == null || typeof msg !== "object") {
408 + return [];
409 + }
410 + const msgObj = msg;
411 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
412 + if (Array.isArray(dataSource.tool_calls)) {
413 + return dataSource.tool_calls;
414 + }
415 + if (dataSource.additional_kwargs && typeof dataSource.additional_kwargs === "object") {
416 + const additionalKwargs = dataSource.additional_kwargs;
417 + if (Array.isArray(additionalKwargs.tool_calls)) {
418 + return additionalKwargs.tool_calls.map((toolCall, idx) => {
419 + let args;
420 + try {
421 + args = toolCall.function && toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
422 + } catch {
423 + args = {};
424 + }
425 + return {
426 + id: toolCall.id ?? `call_${idx}`,
427 + name: toolCall.function && toolCall.function.name ? toolCall.function.name : "unknown",
428 + args
429 + };
430 + });
431 + }
432 + }
433 + return [];
434 +}
435 +function getAdditionalKwargs(msg) {
436 + if (msg == null || typeof msg !== "object") {
437 + return void 0;
438 + }
439 + const msgObj = msg;
440 + const dataSource = msgObj.type === "constructor" && msgObj.kwargs && typeof msgObj.kwargs === "object" ? msgObj.kwargs : msgObj;
441 + return dataSource.additional_kwargs;
442 +}
443 +function baseMessagesToUIMessages(messages) {
444 + const result = [];
445 + let currentAssistant = null;
446 + for (const msg of messages) {
447 + const msgType = getMessageType(msg);
448 + const msgId = getMessageId(msg) ?? generateId();
449 + switch (msgType) {
450 + case "human": {
451 + currentAssistant = null;
452 + const text = getMessageText(msg);
453 + result.push({
454 + id: msgId,
455 + role: "user",
456 + parts: [{ type: "text", text }]
457 + });
458 + break;
459 + }
460 + case "system": {
461 + currentAssistant = null;
462 + const text = getMessageText(msg);
463 + result.push({
464 + id: msgId,
465 + role: "system",
466 + parts: [{ type: "text", text }]
467 + });
468 + break;
469 + }
470 + case "ai": {
471 + const parts = [];
472 + const reasoning = extractReasoningFromContentBlocks(msg) || extractReasoningFromValuesMessage(msg);
473 + if (reasoning) {
474 + parts.push({ type: "reasoning", text: reasoning, state: "done" });
475 + }
476 + const text = getMessageText(msg);
477 + if (text) {
478 + parts.push({ type: "text", text });
479 + }
480 + const additionalKwargs = getAdditionalKwargs(msg);
481 + const imageOutputs = extractImageOutputs(additionalKwargs);
482 + for (const imageOutput of imageOutputs) {
483 + if (imageOutput.result) {
484 + const mediaType = `image/${imageOutput.output_format ?? "png"}`;
485 + parts.push({
486 + type: "file",
487 + mediaType,
488 + url: `data:${mediaType};base64,${imageOutput.result}`
489 + });
490 + }
491 + }
492 + const toolCalls = getToolCalls(msg);
493 + for (const toolCall of toolCalls) {
494 + parts.push({
495 + type: "dynamic-tool",
496 + toolCallId: toolCall.id,
497 + toolName: toolCall.name,
498 + state: "input-available",
499 + input: toolCall.args
500 + });
501 + }
502 + const assistantMessage = {
503 + id: msgId,
504 + role: "assistant",
505 + parts
506 + };
507 + result.push(assistantMessage);
508 + currentAssistant = assistantMessage;
509 + break;
510 + }
511 + case "tool": {
512 + const toolCallId = getToolCallId(msg);
513 + if (toolCallId && currentAssistant) {
514 + const toolPart = currentAssistant.parts.find((part) => part.type === "dynamic-tool" && part.toolCallId === toolCallId);
515 + if (toolPart) {
516 + const idx = currentAssistant.parts.indexOf(toolPart);
517 + const content = getMessageText(msg);
518 + currentAssistant.parts[idx] = {
519 + type: "dynamic-tool",
520 + toolCallId: toolPart.toolCallId,
521 + toolName: toolPart.toolName,
522 + state: "output-available",
523 + input: toolPart.input,
524 + output: content
525 + };
526 + }
527 + }
528 + break;
529 + }
530 + }
531 + }
532 + return result;
533 +}
534 +function extractInterruptParts(tasks) {
535 + if (!Array.isArray(tasks)) {
536 + return [];
537 + }
538 + const parts = [];
539 + for (const task of tasks) {
540 + if (!Array.isArray(task.interrupts)) {
541 + continue;
542 + }
543 + for (const interrupt of task.interrupts) {
544 + if (!interrupt.value || typeof interrupt.value !== "object") {
545 + continue;
546 + }
547 + const interruptValue = interrupt.value;
548 + const actionRequests = interruptValue.actionRequests ?? interruptValue.action_requests;
549 + if (!Array.isArray(actionRequests)) {
550 + continue;
551 + }
552 + for (const request of actionRequests) {
553 + parts.push({
554 + type: "dynamic-tool",
555 + toolCallId: request.id ?? generateId(),
556 + toolName: request.name,
557 + state: "input-available",
558 + input: request.args ?? request.arguments ?? {}
559 + });
560 + }
561 + }
562 + }
563 + return parts;
564 +}
565 +function stateSnapshotToUIMessages(snapshot) {
566 + var _a;
567 + const messages = (_a = snapshot.values) == null ? void 0 : _a.messages;
568 + if (!Array.isArray(messages)) {
569 + return [];
570 + }
571 + const uiMessages = baseMessagesToUIMessages(messages);
572 + const interruptParts = extractInterruptParts(snapshot.tasks);
573 + if (interruptParts.length > 0) {
574 + let lastAssistant = uiMessages.findLast((message) => message.role === "assistant");
575 + if (!lastAssistant) {
576 + lastAssistant = {
577 + id: generateId(),
578 + role: "assistant",
579 + parts: []
580 + };
581 + uiMessages.push(lastAssistant);
582 + }
583 + const existingToolCallIds = new Set();
584 + const existingToolParts = new Set();
585 + for (const part of lastAssistant.parts) {
586 + if (part.type === "dynamic-tool") {
587 + existingToolCallIds.add(part.toolCallId);
588 + existingToolParts.add(`${part.toolName}:${JSON.stringify(part.input)}`);
589 + }
590 + }
591 + for (const interruptPart of interruptParts) {
592 + const toolCallKey = `${interruptPart.toolName}:${JSON.stringify(interruptPart.input)}`;
593 + if (!existingToolCallIds.has(interruptPart.toolCallId) && !existingToolParts.has(toolCallKey)) {
594 + lastAssistant.parts.push(interruptPart);
595 + }
596 + }
597 + }
598 + return uiMessages;
599 +}
600
601 // src/transport.ts
602 import {
603 @@ -1080,8 +1311,10 @@ var LangSmithDeploymentTransport = class {
604 };
605 export {
606 LangSmithDeploymentTransport,
607 + baseMessagesToUIMessages,
608 convertModelMessages,
609 + stateSnapshotToUIMessages,
610 toBaseMessages,
611 - toUIMessageStream
612 + toUIMessageStream
613 };
614 //# sourceMappingURL=index.mjs.map
615 \ No newline at end of file
616 diff --git a/src/adapter.ts b/src/adapter.ts
617 index 0994b4b8..033361d2 100644
618 --- a/src/adapter.ts
619 +++ b/src/adapter.ts
620 @@ -7,6 +7,7 @@ import {
621 type UIMessage,
622 type UIMessageChunk,
623 convertToModelMessages,
624 + generateId,
625 type ModelMessage,
626 } from 'ai';
627 import {
628 @@ -18,6 +19,10 @@ import {
629 parseLangGraphEvent,
630 isToolResultPart,
631 extractReasoningFromContentBlocks,
632 + getMessageText,
633 + getMessageId,
634 + extractReasoningFromValuesMessage,
635 + extractImageOutputs,
636 } from './utils';
637 import { type LangGraphEventState } from './types';
638 import { type StreamCallbacks } from './stream-callbacks';
639 @@ -571,3 +576,385 @@ export function toUIMessageStream<TState = unknown>(
640 },
641 });
642 }
643 +
644 +function getMessageType(msg: unknown): string | undefined {
645 + if (msg == null || typeof msg !== 'object') {
646 + return undefined;
647 + }
648 +
649 + const msgObj = msg as Record<string, unknown>;
650 +
651 + if (typeof msgObj._getType === 'function') {
652 + return (msgObj._getType as () => string)();
653 + }
654 +
655 + if (typeof msgObj.type === 'string' && msgObj.type !== 'constructor') {
656 + return msgObj.type;
657 + }
658 +
659 + if (msgObj.type === 'constructor' && Array.isArray(msgObj.id)) {
660 + const ids = msgObj.id as string[];
661 +
662 + if (ids.includes('HumanMessage') || ids.includes('HumanMessageChunk')) {
663 + return 'human';
664 + }
665 +
666 + if (ids.includes('AIMessage') || ids.includes('AIMessageChunk')) {
667 + return 'ai';
668 + }
669 +
670 + if (ids.includes('SystemMessage') || ids.includes('SystemMessageChunk')) {
671 + return 'system';
672 + }
673 +
674 + if (ids.includes('ToolMessage') || ids.includes('ToolMessageChunk')) {
675 + return 'tool';
676 + }
677 + }
678 +
679 + return undefined;
680 +}
681 +
682 +function getToolCallId(msg: unknown): string | undefined {
683 + if (msg == null || typeof msg !== 'object') {
684 + return undefined;
685 + }
686 +
687 + const msgObj = msg as Record<string, unknown>;
688 + const dataSource =
689 + msgObj.type === 'constructor' &&
690 + msgObj.kwargs &&
691 + typeof msgObj.kwargs === 'object'
692 + ? (msgObj.kwargs as Record<string, unknown>)
693 + : msgObj;
694 +
695 + return typeof dataSource.tool_call_id === 'string'
696 + ? dataSource.tool_call_id
697 + : undefined;
698 +}
699 +
700 +function getToolCalls(
701 + msg: unknown,
702 +): Array<{ id: string; name: string; args: Record<string, unknown> }> {
703 + if (msg == null || typeof msg !== 'object') {
704 + return [];
705 + }
706 +
707 + const msgObj = msg as Record<string, unknown>;
708 + const dataSource =
709 + msgObj.type === 'constructor' &&
710 + msgObj.kwargs &&
711 + typeof msgObj.kwargs === 'object'
712 + ? (msgObj.kwargs as Record<string, unknown>)
713 + : msgObj;
714 +
715 + if (Array.isArray(dataSource.tool_calls)) {
716 + return dataSource.tool_calls as Array<{
717 + id: string;
718 + name: string;
719 + args: Record<string, unknown>;
720 + }>;
721 + }
722 +
723 + if (
724 + dataSource.additional_kwargs &&
725 + typeof dataSource.additional_kwargs === 'object'
726 + ) {
727 + const additionalKwargs = dataSource.additional_kwargs as Record<
728 + string,
729 + unknown
730 + >;
731 +
732 + if (Array.isArray(additionalKwargs.tool_calls)) {
733 + return (
734 + additionalKwargs.tool_calls as Array<{
735 + id?: string;
736 + function?: { name?: string; arguments?: string };
737 + }>
738 + ).map((toolCall, idx) => {
739 + let args: Record<string, unknown>;
740 +
741 + try {
742 + args = toolCall.function?.arguments
743 + ? (JSON.parse(toolCall.function.arguments) as Record<
744 + string,
745 + unknown
746 + >)
747 + : {};
748 + } catch {
749 + args = {};
750 + }
751 +
752 + return {
753 + id: toolCall.id ?? `call_${idx}`,
754 + name: toolCall.function?.name ?? 'unknown',
755 + args,
756 + };
757 + });
758 + }
759 + }
760 +
761 + return [];
762 +}
763 +
764 +function getAdditionalKwargs(
765 + msg: unknown,
766 +): Record<string, unknown> | undefined {
767 + if (msg == null || typeof msg !== 'object') {
768 + return undefined;
769 + }
770 +
771 + const msgObj = msg as Record<string, unknown>;
772 + const dataSource =
773 + msgObj.type === 'constructor' &&
774 + msgObj.kwargs &&
775 + typeof msgObj.kwargs === 'object'
776 + ? (msgObj.kwargs as Record<string, unknown>)
777 + : msgObj;
778 +
779 + return dataSource.additional_kwargs as Record<string, unknown> | undefined;
780 +}
781 +
782 +export function baseMessagesToUIMessages(messages: BaseMessage[]): UIMessage[] {
783 + const result: UIMessage[] = [];
784 + let currentAssistant: UIMessage | null = null;
785 +
786 + for (const msg of messages) {
787 + const msgType = getMessageType(msg);
788 + const msgId = getMessageId(msg) ?? generateId();
789 +
790 + switch (msgType) {
791 + case 'human': {
792 + currentAssistant = null;
793 + const text = getMessageText(msg);
794 +
795 + result.push({
796 + id: msgId,
797 + role: 'user',
798 + parts: [{ type: 'text', text }],
799 + });
800 + break;
801 + }
802 +
803 + case 'system': {
804 + currentAssistant = null;
805 + const text = getMessageText(msg);
806 +
807 + result.push({
808 + id: msgId,
809 + role: 'system',
810 + parts: [{ type: 'text', text }],
811 + });
812 + break;
813 + }
814 +
815 + case 'ai': {
816 + const parts: UIMessage['parts'] = [];
817 +
818 + const reasoning =
819 + extractReasoningFromContentBlocks(msg) ||
820 + extractReasoningFromValuesMessage(msg);
821 + if (reasoning) {
822 + parts.push({ type: 'reasoning', text: reasoning, state: 'done' });
823 + }
824 +
825 + const text = getMessageText(msg);
826 + if (text) {
827 + parts.push({ type: 'text', text });
828 + }
829 +
830 + const additionalKwargs = getAdditionalKwargs(msg);
831 + const imageOutputs = extractImageOutputs(additionalKwargs);
832 + for (const imageOutput of imageOutputs) {
833 + if (imageOutput.result) {
834 + const mediaType = `image/${imageOutput.output_format ?? 'png'}`;
835 + parts.push({
836 + type: 'file',
837 + mediaType,
838 + url: `data:${mediaType};base64,${imageOutput.result}`,
839 + });
840 + }
841 + }
842 +
843 + const toolCalls = getToolCalls(msg);
844 + for (const toolCall of toolCalls) {
845 + parts.push({
846 + type: 'dynamic-tool',
847 + toolCallId: toolCall.id,
848 + toolName: toolCall.name,
849 + state: 'input-available',
850 + input: toolCall.args,
851 + });
852 + }
853 +
854 + const assistantMessage: UIMessage = {
855 + id: msgId,
856 + role: 'assistant',
857 + parts,
858 + };
859 +
860 + result.push(assistantMessage);
861 + currentAssistant = assistantMessage;
862 + break;
863 + }
864 +
865 + case 'tool': {
866 + const toolCallId = getToolCallId(msg);
867 +
868 + if (toolCallId && currentAssistant) {
869 + const toolPart = currentAssistant.parts.find(
870 + (
871 + part,
872 + ): part is Extract<
873 + UIMessage['parts'][number],
874 + { type: 'dynamic-tool' }
875 + > => part.type === 'dynamic-tool' && part.toolCallId === toolCallId,
876 + );
877 +
878 + if (toolPart) {
879 + const idx = currentAssistant.parts.indexOf(toolPart);
880 + const content = getMessageText(msg);
881 +
882 + currentAssistant.parts[idx] = {
883 + type: 'dynamic-tool',
884 + toolCallId: toolPart.toolCallId,
885 + toolName: toolPart.toolName,
886 + state: 'output-available',
887 + input: toolPart.input,
888 + output: content,
889 + };
890 + }
891 + }
892 +
893 + break;
894 + }
895 + }
896 + }
897 +
898 + return result;
899 +}
900 +
901 +interface InterruptActionRequest {
902 + name: string;
903 + args?: Record<string, unknown>;
904 + arguments?: Record<string, unknown>;
905 + id?: string;
906 +}
907 +
908 +interface InterruptValue {
909 + actionRequests?: InterruptActionRequest[];
910 + action_requests?: InterruptActionRequest[];
911 +}
912 +
913 +interface PregelInterrupt {
914 + value?: InterruptValue | unknown;
915 +}
916 +
917 +interface PregelTask {
918 + interrupts?: PregelInterrupt[];
919 + [key: string]: unknown;
920 +}
921 +
922 +interface StateSnapshotLike {
923 + values: { messages?: BaseMessage[] } & Record<string, unknown>;
924 + tasks?: PregelTask[];
925 +}
926 +
927 +function extractInterruptParts(tasks?: PregelTask[]): Array<{
928 + type: 'dynamic-tool';
929 + toolCallId: string;
930 + toolName: string;
931 + state: 'input-available';
932 + input: Record<string, unknown>;
933 +}> {
934 + if (!Array.isArray(tasks)) {
935 + return [];
936 + }
937 +
938 + const parts: Array<{
939 + type: 'dynamic-tool';
940 + toolCallId: string;
941 + toolName: string;
942 + state: 'input-available';
943 + input: Record<string, unknown>;
944 + }> = [];
945 +
946 + for (const task of tasks) {
947 + if (!Array.isArray(task.interrupts)) {
948 + continue;
949 + }
950 +
951 + for (const interrupt of task.interrupts) {
952 + if (!interrupt.value || typeof interrupt.value !== 'object') {
953 + continue;
954 + }
955 +
956 + const interruptValue = interrupt.value as InterruptValue;
957 + const actionRequests =
958 + interruptValue.actionRequests ?? interruptValue.action_requests;
959 +
960 + if (!Array.isArray(actionRequests)) {
961 + continue;
962 + }
963 +
964 + for (const request of actionRequests) {
965 + parts.push({
966 + type: 'dynamic-tool',
967 + toolCallId: request.id ?? generateId(),
968 + toolName: request.name,
969 + state: 'input-available',
970 + input: request.args ?? request.arguments ?? {},
971 + });
972 + }
973 + }
974 + }
975 +
976 + return parts;
977 +}
978 +
979 +export function stateSnapshotToUIMessages(
980 + snapshot: StateSnapshotLike,
981 +): UIMessage[] {
982 + const messages = snapshot.values?.messages;
983 + if (!Array.isArray(messages)) {
984 + return [];
985 + }
986 +
987 + const uiMessages = baseMessagesToUIMessages(messages);
988 + const interruptParts = extractInterruptParts(snapshot.tasks);
989 +
990 + if (interruptParts.length > 0) {
991 + let lastAssistant = uiMessages.findLast(message => message.role === 'assistant');
992 +
993 + if (!lastAssistant) {
994 + lastAssistant = {
995 + id: generateId(),
996 + role: 'assistant',
997 + parts: [],
998 + };
999 + uiMessages.push(lastAssistant);
1000 + }
1001 +
1002 + const existingToolCallIds = new Set<string>();
1003 + const existingToolParts = new Set<string>();
1004 +
1005 + for (const part of lastAssistant.parts) {
1006 + if (part.type === 'dynamic-tool') {
1007 + existingToolCallIds.add(part.toolCallId);
1008 + existingToolParts.add(`${part.toolName}:${JSON.stringify(part.input)}`);
1009 + }
1010 + }
1011 +
1012 + for (const interruptPart of interruptParts) {
1013 + const toolCallKey = `${interruptPart.toolName}:${JSON.stringify(interruptPart.input)}`;
1014 + if (
1015 + !existingToolCallIds.has(interruptPart.toolCallId) &&
1016 + !existingToolParts.has(toolCallKey)
1017 + ) {
1018 + lastAssistant.parts.push(interruptPart);
1019 + }
1020 + }
1021 + }
1022 +
1023 + return uiMessages;
1024 +}
1025 diff --git a/src/index.ts b/src/index.ts
1026 index 76ad34b8..f45829c8 100644
1027 --- a/src/index.ts
1028 +++ b/src/index.ts
1029 @@ -1,6 +1,8 @@
1030 export {
1031 + baseMessagesToUIMessages,
1032 + stateSnapshotToUIMessages,
1033 toBaseMessages,
1034 - toUIMessageStream,
1035 + toUIMessageStream,
1036 convertModelMessages,
1037 } from './adapter';
1038
1039
1039 lines Plain Text