UNPKG

assistant-stream

Version:

Streaming utilities for AI assistants

224 lines (223 loc) • 6.88 kB
import { generateId } from "../../utils/generateId.js"; // src/core/serialization/ui-message-stream/chunk-normalizer.ts const createChunkNormalizer = () => { const pendingTools = /* @__PURE__ */ new Map(); const createPendingTool = (toolCallId, toolName) => ({ toolCallId, toolName: typeof toolName === "string" ? toolName : "", deltas: [], emitted: false, hasFinalResult: false, pendingResults: [] }); const emitPendingTool = (out, tool) => { if (!tool.emitted) { tool.emitted = true; out.enqueue({ type: "tool-call-start", id: generateId(), toolCallId: tool.toolCallId, toolName: tool.toolName }); for (const argsText of tool.deltas) out.enqueue({ type: "tool-call-delta", argsText }); out.enqueue({ type: "tool-call-end" }); } while (tool.pendingResults.length > 0) { const pendingResult = tool.pendingResults.shift(); out.enqueue({ type: "tool-result", toolCallId: tool.toolCallId, result: pendingResult.result, ...pendingResult.isError ? { isError: true } : {}, ...pendingResult.isPreliminary ? { isPreliminary: true } : {} }); } }; return { normalize(chunk, controller) { if (chunk.type === "abort") { pendingTools.clear(); controller.enqueue({ type: "finish", finishReason: "other", usage: { inputTokens: 0, outputTokens: 0 } }); return; } if (chunk.type === "text-delta" && chunk.textDelta === void 0) { const { delta, ...rest } = chunk; controller.enqueue({ ...rest, type: "text-delta", textDelta: delta ?? "" }); return; } if (chunk.type === "start") { controller.enqueue({ ...chunk, type: "start", messageId: chunk.messageId ?? generateId() }); return; } if (chunk.type === "source-url") { controller.enqueue({ type: "source", source: { sourceType: "url", id: chunk.sourceId, url: chunk.url, ...chunk.title && { title: chunk.title } } }); return; } if (chunk.type === "source" && chunk.source == null) return; if (chunk.type === "file" && chunk.file == null) { if (chunk.url === void 0) return; controller.enqueue({ type: "file", file: { mimeType: chunk.mediaType, data: chunk.url } }); return; } if (chunk.type === "finish-step") { controller.enqueue({ ...chunk, type: "finish-step", finishReason: chunk.finishReason ?? "unknown", usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 }, isContinued: chunk.isContinued ?? false }); return; } if (chunk.type === "finish") { controller.enqueue({ ...chunk, type: "finish", finishReason: chunk.finishReason ?? "unknown", usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 } }); return; } if (chunk.type === "tool-input-start") { if (typeof chunk.toolCallId !== "string") return; if (pendingTools.has(chunk.toolCallId)) return; pendingTools.set(chunk.toolCallId, createPendingTool(chunk.toolCallId, chunk.toolName)); return; } if (chunk.type === "tool-input-delta") { if (typeof chunk.toolCallId !== "string") return; if (typeof chunk.inputTextDelta !== "string") return; const tool = pendingTools.get(chunk.toolCallId); if (!tool || tool.emitted) return; tool.deltas.push(chunk.inputTextDelta); return; } if (chunk.type === "tool-input-available" || chunk.type === "tool-input-error") { if (typeof chunk.toolCallId !== "string") return; let tool = pendingTools.get(chunk.toolCallId); if (!tool) { tool = createPendingTool(chunk.toolCallId, chunk.toolName); pendingTools.set(chunk.toolCallId, tool); } if (tool.emitted) return; if (chunk.input !== void 0) { const isUnparsedInputText = chunk.type === "tool-input-error" && typeof chunk.input === "string"; tool.deltas = [isUnparsedInputText ? chunk.input : JSON.stringify(chunk.input)]; } if (chunk.type === "tool-input-error") { tool.pendingResults.push({ result: typeof chunk.errorText === "string" ? chunk.errorText : "", isError: true }); tool.hasFinalResult = true; } emitPendingTool(controller, tool); return; } if (chunk.type === "tool-output-available" || chunk.type === "tool-output-error") { if (typeof chunk.toolCallId !== "string") return; if (chunk.type === "tool-output-available" && !Object.hasOwn(chunk, "output")) return; let tool = pendingTools.get(chunk.toolCallId); const opensCall = tool === void 0; if (!tool) { tool = createPendingTool(chunk.toolCallId, ""); pendingTools.set(chunk.toolCallId, tool); } if (tool.hasFinalResult) return; const pendingResult = chunk.type === "tool-output-error" ? { result: typeof chunk.errorText === "string" ? chunk.errorText : "", isError: true } : { result: chunk.output, isError: false, ...chunk.preliminary ? { isPreliminary: true } : {} }; tool.pendingResults.push(pendingResult); if (!pendingResult.isPreliminary) tool.hasFinalResult = true; if (tool.emitted || opensCall) emitPendingTool(controller, tool); return; } if (chunk.type === "tool-approval-request") { if (typeof chunk.toolCallId !== "string" || typeof chunk.approvalId !== "string") return; controller.enqueue({ type: "tool-approval-request", toolCallId: chunk.toolCallId, approval: { id: chunk.approvalId, ...chunk.approvalDescriptor != null && { descriptor: chunk.approvalDescriptor }, ...Object.hasOwn(chunk, "inputSchemaInput") && { inputSchemaInput: chunk.inputSchemaInput }, ...typeof chunk.reason === "string" && { prompt: chunk.reason, requestReason: chunk.reason }, ...typeof chunk.isAutomatic === "boolean" && { isAutomatic: chunk.isAutomatic }, ...typeof chunk.signature === "string" && { signature: chunk.signature } } }); return; } if (chunk.type === "tool-approval-response") { if (typeof chunk.approvalId !== "string" || typeof chunk.approved !== "boolean") return; controller.enqueue({ type: "tool-approval-response", approvalId: chunk.approvalId, approved: chunk.approved, ...typeof chunk.reason === "string" && { reason: chunk.reason } }); return; } if (chunk.type === "tool-output-denied") { if (typeof chunk.toolCallId !== "string") return; controller.enqueue({ type: "tool-output-denied", toolCallId: chunk.toolCallId }); return; } controller.enqueue(chunk); }, flush(controller) { for (const tool of pendingTools.values()) emitPendingTool(controller, tool); } }; }; export { createChunkNormalizer }; //# sourceMappingURL=chunk-normalizer.js.map