UNPKG

@convex-dev/agent

Version:

A agent component for Convex.

211 lines 9.29 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, createdAt: new Date(message._creationTime), order: message.order, stepOrder: message.stepOrder, status: message.streaming ? "streaming" : message.status, key: `${message.threadId}-${message.order}-${message.stepOrder}`, text, }; const partCommon = { state: message.streaming ? "streaming" : "done", ...(message.providerMetadata ? { providerMetadata: message.providerMetadata } : {}), }; if (coreMessage.role === "system") { uiMessages.push({ ...common, role: "system", agentName: message.agentName, parts: [{ type: "text", text, ...partCommon }], }); } 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", parts, }); } else { if (coreMessage.role === "tool" && !assistantMessage) { console.warn("Tool message without preceding assistant message.. skipping", message); continue; } if (!assistantMessage) { assistantMessage = { ...common, role: "assistant", agentName: message.agentName, parts: [], }; uiMessages.push(assistantMessage); } else { assistantMessage.status = message.streaming ? "streaming" : message.status; } // update it to the last message's id assistantMessage.id = message._id; if (message.reasoning && !nonStringContent.some((c) => c.type === "reasoning")) { assistantMessage.parts.push({ type: "reasoning", text: message.reasoning, ...partCommon, }); } if (message.text && !nonStringContent.length) { assistantMessage.parts.push({ type: "text", text: message.text, ...partCommon, }); } for (const source of message.sources ?? []) { if (source.sourceType === "url") { assistantMessage.parts.push({ type: "source-url", url: source.url, sourceId: source.id, providerMetadata: message.providerMetadata, title: source.title, }); } else { assistantMessage.parts.push({ type: "source-document", mediaType: source.mediaType, sourceId: source.id, title: source.title, filename: source.filename, providerMetadata: message.providerMetadata, }); } } for (const contentPart of nonStringContent) { switch (contentPart.type) { case "text": assistantMessage.parts.push({ ...partCommon, ...contentPart, }); break; case "reasoning": assistantMessage.parts.push({ ...partCommon, ...contentPart, }); break; case "file": case "image": assistantMessage.parts.push(toUIFilePart(contentPart)); break; case "tool-call": { assistantMessage.parts.push({ type: "step-start", }); const toolPart = { type: `tool-${contentPart.toolName}`, toolCallId: contentPart.toolCallId, input: contentPart.input, providerExecuted: contentPart.providerExecuted, ...(message.streaming ? { state: "input-streaming" } : { state: "input-available", callProviderMetadata: message.providerMetadata, }), }; assistantMessage.parts.push(toolPart); break; } case "tool-result": { const call = assistantMessage.parts.find((part) => part.type === `tool-${contentPart.toolName}` && "toolCallId" in part && part.toolCallId === contentPart.toolCallId); if (call) { if (message.error) { call.state = "output-error"; call.errorText = message.error; call.output = contentPart.output; } else { call.state = "output-available"; call.output = contentPart.output?.type === "json" ? contentPart.output.value : contentPart.output; // Technically we could pull this from the doc.message // but the ModelMessage doesn't have it // call.providerExecuted = contentPart.providerExecuted; } } else { console.warn("Tool result without preceding tool call.. adding anyways", contentPart); if (message.error) { assistantMessage.parts.push({ type: `tool-${contentPart.toolName}`, toolCallId: contentPart.toolCallId, state: "output-error", input: undefined, errorText: message.error, // Technically we could pull this from the doc.message // but the ModelMessage doesn't have it // providerExecuted: contentPart.providerExecuted, callProviderMetadata: message.providerMetadata, }); } else { assistantMessage.parts.push({ type: `tool-${contentPart.toolName}`, toolCallId: contentPart.toolCallId, state: "output-available", input: undefined, output: contentPart.output?.type === "json" ? contentPart.output.value : contentPart.output, callProviderMetadata: message.providerMetadata, }); } } 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