UNPKG

agents

Version:

A home for your AI agents

317 lines (316 loc) • 9.89 kB
//#region src/harness/ai-sdk/turns.ts function ownerOf(toolName, { tools, owner }) { const tool = tools && Object.hasOwn(tools, toolName) ? tools[toolName] : void 0; return tool && !tool.execute ? owner : void 0; } const json = (value) => value ?? null; const object = (value) => typeof value === "object" && value !== null ? value : void 0; function withMetadata(value, metadata) { const providerMetadata = object(metadata); return providerMetadata ? { ...value, providerMetadata } : value; } let warned = false; function warnSkipped(type) { if (warned) return; warned = true; console.warn(`Channels skipped an AI SDK part it cannot carry: "${type}"`); } /** * Convert an AI SDK UI message stream into response chunks. * * An `error` or `abort` chunk makes the iteration throw once the stream * ends, so a failed or aborted generation never reads as a finished one, while the stream still * runs to its end and its `onEnd` sees the partial message. */ async function* toResponseChunks(stream, options = {}) { let error; for await (const chunk of stream) { if (chunk.type === "error") error ??= new Error(chunk.errorText); if (chunk.type === "abort") error ??= /* @__PURE__ */ new Error("The generation was aborted"); const converted = toResponseChunk(chunk, options); if (converted) yield converted; } if (error) throw error; } /** Convert one AI SDK UI message chunk into a response chunk, if it has one. */ function toResponseChunk(chunk, options) { switch (chunk.type) { case "text-start": case "text-end": case "reasoning-start": case "reasoning-end": return withMetadata({ type: chunk.type, id: chunk.id }, chunk.providerMetadata); case "text-delta": case "reasoning-delta": return { type: chunk.type, id: chunk.id, delta: chunk.delta }; case "tool-input-start": return tool(chunk, options, { type: "tool-input-start" }); case "tool-input-delta": return { type: "tool-input-delta", toolCallId: chunk.toolCallId, delta: chunk.inputTextDelta }; case "tool-input-available": return withMetadata(tool(chunk, options, { type: "tool-input-available", input: json(chunk.input) }), chunk.providerMetadata); case "tool-input-error": { const { owner: _owner, ...call } = tool(chunk, {}, {}); return { ...call, type: "tool-input-error", input: json(chunk.input), errorText: chunk.errorText }; } case "tool-approval-request": return { type: "tool-approval-request", toolCallId: chunk.toolCallId, approvalId: chunk.approvalId }; case "tool-output-available": return { type: "tool-output-available", toolCallId: chunk.toolCallId, output: json(chunk.output), ...chunk.preliminary && { preliminary: true }, ...chunk.providerExecuted && { providerExecuted: true } }; case "tool-output-error": return { type: "tool-output-error", toolCallId: chunk.toolCallId, errorText: chunk.errorText, ...chunk.providerExecuted && { providerExecuted: true } }; case "tool-output-denied": return { type: "tool-output-denied", toolCallId: chunk.toolCallId }; case "source-url": case "source-document": case "file": { const { providerMetadata, ...part } = chunk; return withMetadata(part, providerMetadata); } case "start": case "finish": return chunk.messageMetadata === void 0 ? void 0 : metadata(chunk.messageMetadata); case "message-metadata": return metadata(chunk.messageMetadata); case "start-step": case "finish-step": case "tool-approval-response": case "error": case "abort": return; default: if (chunk.type.startsWith("data-") && "data" in chunk) return { type: "data", name: chunk.type.slice(5), ...chunk.id !== void 0 && { id: chunk.id }, data: json(chunk.data), ...chunk.transient && { transient: true } }; warnSkipped(chunk.type); return; } } function tool(chunk, options, rest) { const owner = ownerOf(chunk.toolName, options); return { toolCallId: chunk.toolCallId, toolName: chunk.toolName, ...owner !== void 0 && { owner }, ...chunk.providerExecuted && { providerExecuted: true }, ...chunk.dynamic && { dynamic: true }, ...chunk.title !== void 0 && { title: chunk.title }, ...rest }; } function metadata(value) { const converted = object(value); return converted ? { type: "metadata", metadata: converted } : void 0; } /** * Convert a response chunk into an AI SDK UI message chunk. Fields that only * Channels has, such as a tool call's `owner`, are dropped. */ function toUIMessageChunk(chunk) { switch (chunk.type) { case "tool-input-start": case "tool-input-available": { const { owner: _owner, ...rest } = chunk; return rest; } case "tool-input-delta": return { type: "tool-input-delta", toolCallId: chunk.toolCallId, inputTextDelta: chunk.delta }; case "data": { const { name, ...rest } = chunk; return { ...rest, type: `data-${name}` }; } case "metadata": return { type: "message-metadata", messageMetadata: chunk.metadata }; default: return chunk; } } /** Convert a saved AI SDK UI message into a transcript message. */ function toTranscriptMessage(message, options = {}) { const metadata = object(message.metadata); return { id: message.id, role: message.role, parts: message.parts.flatMap((part) => { const converted = toMessagePart(part, options); return converted ? [converted] : []; }), ...metadata && { metadata } }; } function toMessagePart(part, options) { switch (part.type) { case "text": case "reasoning": return withMetadata({ type: part.type, text: part.text }, part.providerMetadata); case "file": case "source-url": case "source-document": { const { providerMetadata, ...rest } = part; if ("providerReference" in rest) delete rest.providerReference; return withMetadata(rest, providerMetadata); } case "step-start": return; case "dynamic-tool": return toToolPart(part, part.toolName, true, options); default: if (part.type.startsWith("tool-") && "toolCallId" in part) return toToolPart(part, part.type.slice(5), false, options); if (part.type.startsWith("data-") && "data" in part) return { type: "data", name: part.type.slice(5), ...part.id !== void 0 && { id: part.id }, data: json(part.data) }; warnSkipped(part.type); return; } } function toToolPart(part, toolName, dynamic, options) { const owner = ownerOf(toolName, options); return withMetadata({ type: "tool", toolCallId: part.toolCallId, toolName, state: part.state, ...part.input !== void 0 && { input: json(part.input) }, ...part.state === "output-available" && { output: json(part.output), ...part.preliminary && { preliminary: true } }, ...part.state === "output-error" && { errorText: part.errorText }, ...part.approval && { approval: { id: part.approval.id, ...part.approval.approved !== void 0 && { approved: part.approval.approved }, ...part.approval.reason !== void 0 && { reason: part.approval.reason } } }, ...owner !== void 0 && { owner }, ...part.providerExecuted && { providerExecuted: true }, ...dynamic && { dynamic: true }, ...part.title !== void 0 && { title: part.title } }, part.callProviderMetadata); } /** Convert a transcript message into an AI SDK UI message. */ function toUIMessage(message) { return { id: message.id, role: message.role, parts: message.parts.map(toUIPart), ...message.metadata && { metadata: message.metadata } }; } function toUIPart(part) { switch (part.type) { case "data": { const { name, ...rest } = part; return { ...rest, type: `data-${name}` }; } case "tool": { const { type: _type, toolName, dynamic, owner: _owner, providerMetadata, ...rest } = part; const call = { ...rest, ...providerMetadata && { callProviderMetadata: providerMetadata } }; return dynamic ? { ...call, type: "dynamic-tool", toolName } : { ...call, type: `tool-${toolName}` }; } default: return part; } } /** Whether the message has a tool call waiting for a result or an approval. */ function awaitsInput(message) { return message.parts.some((part) => "toolCallId" in part && (part.state === "input-available" || part.state === "approval-requested")); } /** * Record a tool result or approval response on the tool call it answers. * Returns the updated message, or undefined when no call is waiting for it, * so the first answer wins. */ function answerToolCall(messages, event) { for (const message of messages) { const index = message.parts.findIndex((part) => answers(part, event)); if (index === -1) continue; const parts = [...message.parts]; const part = parts[index]; parts[index] = event.type === "approval-response" ? { ...part, state: event.approved ? "approval-responded" : "output-denied", approval: { id: event.approvalId, approved: event.approved, ...event.reason !== void 0 && { reason: event.reason } } } : event.type === "tool-result" && event.result.ok ? { ...part, state: "output-available", output: event.result.output } : { ...part, state: "output-error", errorText: event.type === "tool-result" && !event.result.ok ? event.result.errorText ?? "" : "" }; return { ...message, parts }; } } function answers(part, event) { if (!("toolCallId" in part)) return false; if (event.type === "tool-result") return part.toolCallId === event.toolCallId && (part.state === "input-available" || part.state === "approval-responded"); return event.type === "approval-response" && part.state === "approval-requested" && part.approval.id === event.approvalId; } //#endregion export { toTranscriptMessage as a, toResponseChunks as i, awaitsInput as n, toUIMessage as o, toResponseChunk as r, toUIMessageChunk as s, answerToolCall as t }; //# sourceMappingURL=turns-DCV3ycDl.js.map