UNPKG

@convex-dev/agent

Version:

A agent component for Convex.

146 lines 5.99 kB
import { deserializeMessage, toUIFilePart } from "../mapping.js"; export function toUIMessages(messages) { const uiMessages = []; let assistantMessage; for (const message of messages) { const coreMessage = message.message && deserializeMessage(message.message); const text = message.text ?? ""; const content = coreMessage?.content; const nonStringContent = content && typeof content !== "string" ? content : []; if (!coreMessage) continue; const common = { id: message.id ?? message._id, createdAt: new Date(message._creationTime), order: message.order, stepOrder: message.stepOrder, status: message.streaming ? "streaming" : message.status, key: `${message.threadId}-${message.order}-${message.stepOrder}`, }; if (coreMessage.role === "system") { uiMessages.push({ ...common, role: "system", content: text, parts: [{ type: "text", text }], }); } else if (coreMessage.role === "user") { const parts = []; if (text) { parts.push({ type: "text", text }); } nonStringContent.forEach((contentPart) => { switch (contentPart.type) { case "file": case "image": parts.push(toUIFilePart(contentPart)); break; } }); uiMessages.push({ ...common, role: "user", content: message.text ?? "", parts, }); } else { if (coreMessage.role === "tool" && !assistantMessage) { console.warn("Tool message without preceding assistant message.. skipping", message); continue; } if (!assistantMessage) { assistantMessage = { ...common, role: "assistant", content: "", parts: [], }; uiMessages.push(assistantMessage); } else { assistantMessage.status = message.streaming ? "streaming" : message.status; } // update it to the last message's id assistantMessage.id = message.id ?? message._id; if (message.text) { assistantMessage.parts.push({ type: "text", text: message.text, }); assistantMessage.content += message.text; } if (message.reasoning) { assistantMessage.parts.push({ type: "reasoning", reasoning: message.reasoning, details: message.reasoningDetails ?? [], }); } for (const source of message.sources ?? []) { assistantMessage.parts.push({ type: "source", source, }); } for (const contentPart of nonStringContent) { switch (contentPart.type) { case "file": case "image": assistantMessage.parts.push(toUIFilePart(contentPart)); break; case "tool-call": assistantMessage.parts.push({ type: "step-start", }); assistantMessage.parts.push({ type: "tool-invocation", toolInvocation: { state: "call", step: assistantMessage.parts.filter((part) => part.type === "tool-invocation").length, toolCallId: contentPart.toolCallId, toolName: contentPart.toolName, args: contentPart.args, }, }); break; case "tool-result": { const call = assistantMessage.parts.find((part) => part.type === "tool-invocation" && part.toolInvocation.toolCallId === contentPart.toolCallId); const toolInvocation = { state: "result", toolCallId: contentPart.toolCallId, toolName: contentPart.toolName, args: call?.toolInvocation.args, result: contentPart.result, step: call?.toolInvocation.step ?? assistantMessage.parts.filter((part) => part.type === "tool-invocation").length, }; if (call) { call.toolInvocation = toolInvocation; } else { console.warn("Tool result without preceding tool call.. adding anyways", contentPart); assistantMessage.parts.push({ type: "tool-invocation", toolInvocation, }); } break; } } } } if (!message.tool && assistantMessage && assistantMessage.parts.length > 0) { // Reset it so the next set of tool calls will create a new assistant message assistantMessage = undefined; } } return uiMessages; } //# sourceMappingURL=toUIMessages.js.map