UNPKG

@mastra/core

Version:
1,344 lines (1,343 loc) 44.1 kB
import "./llm-DntEbB3j.js"; import { T as DelayedPromise } from "./trip-wire-csEv7lz7.js"; import { n as isUrlString } from "./content-DX_6irdy.js"; import { n as DefaultGeneratedFileWithType, t as DefaultGeneratedFile } from "./file-C2ghzAMN.js"; import EventEmitter$1 from "events"; import { ReadableStream as ReadableStream$1, TransformStream as TransformStream$1, WritableStream as WritableStream$1 } from "stream/web"; //#region src/stream/aisdk/v5/compat/ui-message.ts function convertFullStreamChunkToUIMessageStream({ part, messageMetadataValue, sendReasoning, sendSources, onError, sendStart, sendFinish, responseMessageId }) { const partType = part.type; switch (partType) { case "text-start": return { type: "text-start", id: part.id, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; case "text-delta": return { type: "text-delta", id: part.id, delta: part.text, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; case "text-end": return { type: "text-end", id: part.id, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; case "reasoning-start": return { type: "reasoning-start", id: part.id, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; case "reasoning-delta": if (sendReasoning) return { type: "reasoning-delta", id: part.id, delta: part.text, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; return; case "reasoning-end": return { type: "reasoning-end", id: part.id, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; case "file": { const base64 = part.file.base64; return { type: "file", mediaType: part.file.mediaType, url: isUrlString(base64) ? base64 : `data:${part.file.mediaType};base64,${base64}` }; } case "source": if (sendSources && part.sourceType === "url") return { type: "source-url", sourceId: part.id, url: part.url, title: part.title, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; if (sendSources && part.sourceType === "document") return { type: "source-document", sourceId: part.id, mediaType: part.mediaType, title: part.title, filename: part.filename, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {} }; return; case "tool-input-start": return { type: "tool-input-start", toolCallId: part.id, toolName: part.toolName, ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {}, ...part.dynamic != null ? { dynamic: part.dynamic } : {} }; case "tool-input-delta": return { type: "tool-input-delta", toolCallId: part.id, inputTextDelta: part.delta }; case "tool-call": return { type: "tool-input-available", toolCallId: part.toolCallId, toolName: part.toolName, input: part.input, ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {}, ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}, ...part.dynamic != null ? { dynamic: part.dynamic } : {} }; case "tool-result": return { type: "tool-output-available", toolCallId: part.toolCallId, output: part.output, ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {}, ...part.dynamic != null ? { dynamic: part.dynamic } : {} }; case "tool-output": return { ...part.output }; case "tool-error": return { type: "tool-output-error", toolCallId: part.toolCallId, errorText: onError(part.error), ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {}, ...part.dynamic != null ? { dynamic: part.dynamic } : {} }; case "error": return { type: "error", errorText: onError(part.error) }; case "start-step": return { type: "start-step" }; case "finish-step": return { type: "finish-step" }; case "start": if (sendStart) return { type: "start", ...messageMetadataValue != null ? { messageMetadata: messageMetadataValue } : {}, ...responseMessageId != null ? { messageId: responseMessageId } : {} }; return; case "finish": if (sendFinish) return { type: "finish", ...messageMetadataValue != null ? { messageMetadata: messageMetadataValue } : {} }; return; case "abort": return part; case "tool-input-end": return; case "raw": return; default: throw new Error(`Unknown chunk type: ${partType}`); } } //#endregion //#region src/stream/base/consume-stream.ts async function consumeStream({ stream, onError }) { const reader = stream.getReader(); try { while (true) { const { done } = await reader.read(); if (done) break; } } catch (error) { onError?.(error); } finally { reader.releaseLock(); } } //#endregion //#region src/stream/RunOutput.ts var WorkflowRunOutput = class { #status = "running"; #tripwireData; #usageCount = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheCreationInputTokens: 0, reasoningTokens: 0 }; #consumptionStarted = false; #baseStream; #emitter = new EventEmitter$1(); #bufferedChunks = []; #streamFinished = false; #streamError; #finalWorkflowResult; #delayedPromises = { usage: new DelayedPromise(), result: new DelayedPromise() }; /** * Unique identifier for this workflow run */ runId; /** * Unique identifier for this workflow */ workflowId; constructor({ runId, workflowId, stream }) { const self = this; this.runId = runId; this.workflowId = workflowId; this.#baseStream = stream; stream.pipeTo(new WritableStream$1({ start() { const chunk = { type: "workflow-start", runId: self.runId, from: "WORKFLOW", payload: { workflowId: self.workflowId } }; self.#bufferedChunks.push(chunk); self.#emitter.emit("chunk", chunk); }, write(chunk) { if (chunk.type !== "workflow-step-finish") { self.#bufferedChunks.push(chunk); self.#emitter.emit("chunk", chunk); } if (chunk.type === "workflow-step-output") { if ("output" in chunk.payload && chunk.payload.output) { const output = chunk.payload.output; if (output.type === "finish") { if (output.payload && "usage" in output.payload && output.payload.usage) self.#updateUsageCount(output.payload.usage); else if (output.payload && "output" in output.payload && output.payload.output) { const outputPayload = output.payload.output; if ("usage" in outputPayload && outputPayload.usage) self.#updateUsageCount(outputPayload.usage); } } } } else if (chunk.type === "workflow-canceled") self.#status = "canceled"; else if (chunk.type === "workflow-step-suspended") self.#status = "suspended"; else if (chunk.type === "workflow-step-result" && chunk.payload.status === "failed") if (chunk.payload.tripwire) { self.#status = "tripwire"; self.#tripwireData = chunk.payload.tripwire; } else self.#status = "failed"; else if (chunk.type === "workflow-paused") self.#status = "paused"; }, close() { if (self.#status === "running") self.#status = "success"; self.#emitter.emit("chunk", { type: "workflow-finish", runId: self.runId, from: "WORKFLOW", payload: { workflowStatus: self.#status, metadata: self.#streamError ? { error: self.#streamError, errorMessage: self.#streamError?.message } : {}, output: { usage: self.#usageCount }, ...self.#status === "success" && self.#finalWorkflowResult !== void 0 ? { finalWorkflowResult: self.#finalWorkflowResult } : {}, ...self.#status === "tripwire" && self.#tripwireData ? { tripwire: self.#tripwireData } : {} } }); self.#delayedPromises.usage.resolve(self.#usageCount); Object.entries(self.#delayedPromises).forEach(([key, promise]) => { if (promise.status.type === "pending") promise.reject(/* @__PURE__ */ new Error(`promise '${key}' was not resolved or rejected when stream finished`)); }); self.#streamFinished = true; self.#emitter.emit("finish"); } })).catch((reason) => { console.log(" something went wrong", reason); }); } #getDelayedPromise(promise) { if (!this.#consumptionStarted) this.consumeStream(); return promise.promise; } #updateUsageCount(usage) { let totalUsage = { inputTokens: this.#usageCount.inputTokens ?? 0, outputTokens: this.#usageCount.outputTokens ?? 0, totalTokens: this.#usageCount.totalTokens ?? 0, reasoningTokens: this.#usageCount.reasoningTokens ?? 0, cachedInputTokens: this.#usageCount.cachedInputTokens ?? 0, cacheCreationInputTokens: this.#usageCount.cacheCreationInputTokens ?? 0 }; if ("inputTokens" in usage) { totalUsage.inputTokens += parseInt(usage?.inputTokens?.toString() ?? "0", 10); totalUsage.outputTokens += parseInt(usage?.outputTokens?.toString() ?? "0", 10); } else if ("promptTokens" in usage) { totalUsage.inputTokens += parseInt(usage?.promptTokens?.toString() ?? "0", 10); totalUsage.outputTokens += parseInt(usage?.completionTokens?.toString() ?? "0", 10); } totalUsage.totalTokens += parseInt(usage?.totalTokens?.toString() ?? "0", 10); totalUsage.reasoningTokens += parseInt(usage?.reasoningTokens?.toString() ?? "0", 10); totalUsage.cachedInputTokens += parseInt(usage?.cachedInputTokens?.toString() ?? "0", 10); totalUsage.cacheCreationInputTokens += parseInt(usage?.cacheCreationInputTokens?.toString() ?? "0", 10); this.#usageCount = totalUsage; } /** * @internal */ updateResults(results) { if (results.status === "success") this.#finalWorkflowResult = results.result; this.#delayedPromises.result.resolve(results); } /** * @internal */ rejectResults(error) { this.#delayedPromises.result.reject(error); this.#status = "failed"; this.#streamError = error; } /** * @internal */ resume(stream) { this.#baseStream = stream; this.#streamFinished = false; this.#consumptionStarted = false; this.#status = "running"; this.#delayedPromises = { usage: new DelayedPromise(), result: new DelayedPromise() }; const self = this; stream.pipeTo(new WritableStream$1({ start() { const chunk = { type: "workflow-start", runId: self.runId, from: "WORKFLOW", payload: { workflowId: self.workflowId } }; self.#bufferedChunks.push(chunk); self.#emitter.emit("chunk", chunk); }, write(chunk) { if (chunk.type !== "workflow-step-finish") { self.#bufferedChunks.push(chunk); self.#emitter.emit("chunk", chunk); } if (chunk.type === "workflow-step-output") { if ("output" in chunk.payload && chunk.payload.output) { const output = chunk.payload.output; if (output.type === "finish") { if (output.payload && "usage" in output.payload && output.payload.usage) self.#updateUsageCount(output.payload.usage); else if (output.payload && "output" in output.payload && output.payload.output) { const outputPayload = output.payload.output; if ("usage" in outputPayload && outputPayload.usage) self.#updateUsageCount(outputPayload.usage); } } } } else if (chunk.type === "workflow-canceled") self.#status = "canceled"; else if (chunk.type === "workflow-step-suspended") self.#status = "suspended"; else if (chunk.type === "workflow-step-result" && chunk.payload.status === "failed") if (chunk.payload.tripwire) { self.#status = "tripwire"; self.#tripwireData = chunk.payload.tripwire; } else self.#status = "failed"; else if (chunk.type === "workflow-paused") self.#status = "paused"; }, close() { if (self.#status === "running") self.#status = "success"; self.#emitter.emit("chunk", { type: "workflow-finish", runId: self.runId, from: "WORKFLOW", payload: { workflowStatus: self.#status, metadata: self.#streamError ? { error: self.#streamError, errorMessage: self.#streamError?.message } : {}, output: { usage: self.#usageCount }, ...self.#status === "success" && self.#finalWorkflowResult !== void 0 ? { finalWorkflowResult: self.#finalWorkflowResult } : {}, ...self.#status === "tripwire" && self.#tripwireData ? { tripwire: self.#tripwireData } : {} } }); self.#streamFinished = true; self.#emitter.emit("finish"); } })).catch((reason) => { console.log(" something went wrong", reason); }); } async consumeStream(options) { if (this.#consumptionStarted) return; this.#consumptionStarted = true; try { await consumeStream({ stream: this.#baseStream, onError: options?.onError }); } catch (error) { options?.onError?.(error); } } get fullStream() { const self = this; let detach; return new ReadableStream$1({ start(controller) { self.#bufferedChunks.forEach((chunk) => { controller.enqueue(chunk); }); if (self.#streamFinished) { controller.close(); return; } const chunkHandler = (chunk) => { controller.enqueue(chunk); }; const finishHandler = () => { self.#emitter.off("chunk", chunkHandler); self.#emitter.off("finish", finishHandler); controller.close(); }; self.#emitter.on("chunk", chunkHandler); self.#emitter.on("finish", finishHandler); detach = () => { self.#emitter.off("chunk", chunkHandler); self.#emitter.off("finish", finishHandler); }; }, pull(_controller) { if (!self.#consumptionStarted) self.consumeStream(); }, cancel() { detach?.(); } }); } get status() { return this.#status; } get result() { return this.#getDelayedPromise(this.#delayedPromises.result); } get usage() { return this.#getDelayedPromise(this.#delayedPromises.usage); } /** * @deprecated Use `fullStream.locked` instead */ get locked() { console.warn("WorkflowRunOutput.locked is deprecated. Use fullStream.locked instead."); return this.fullStream.locked; } /** * @deprecated Use `fullStream.cancel()` instead */ cancel(reason) { console.warn("WorkflowRunOutput.cancel() is deprecated. Use fullStream.cancel() instead."); return this.fullStream.cancel(reason); } /** * @deprecated Use `fullStream.getReader()` instead */ getReader(options) { console.warn("WorkflowRunOutput.getReader() is deprecated. Use fullStream.getReader() instead."); return this.fullStream.getReader(options); } /** * @deprecated Use `fullStream.pipeThrough()` instead */ pipeThrough(transform, options) { console.warn("WorkflowRunOutput.pipeThrough() is deprecated. Use fullStream.pipeThrough() instead."); return this.fullStream.pipeThrough(transform, options); } /** * @deprecated Use `fullStream.pipeTo()` instead */ pipeTo(destination, options) { console.warn("WorkflowRunOutput.pipeTo() is deprecated. Use fullStream.pipeTo() instead."); return this.fullStream.pipeTo(destination, options); } /** * @deprecated Use `fullStream.tee()` instead */ tee() { console.warn("WorkflowRunOutput.tee() is deprecated. Use fullStream.tee() instead."); return this.fullStream.tee(); } /** * @deprecated Use `fullStream[Symbol.asyncIterator]()` instead */ [Symbol.asyncIterator]() { console.warn("WorkflowRunOutput[Symbol.asyncIterator]() is deprecated. Use fullStream[Symbol.asyncIterator]() instead."); return this.fullStream[Symbol.asyncIterator](); } /** * Helper method to treat this object as a ReadableStream * @deprecated Use `fullStream` directly instead */ toReadableStream() { console.warn("WorkflowRunOutput.toReadableStream() is deprecated. Use fullStream directly instead."); return this.fullStream; } }; //#endregion //#region src/stream/MastraAgentNetworkStream.ts var MastraAgentNetworkStream = class extends ReadableStream$1 { #usageCount = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheCreationInputTokens: 0, reasoningTokens: 0 }; #streamPromise; #objectPromise; #objectStreamController = null; #objectStream = null; #run; runId; constructor({ createStream, run }) { const deferredPromise = { promise: null, resolve: null, reject: null }; deferredPromise.promise = new Promise((resolve, reject) => { deferredPromise.resolve = resolve; deferredPromise.reject = reject; }); const objectDeferredPromise = { promise: null, resolve: null, reject: null }; objectDeferredPromise.promise = new Promise((resolve, reject) => { objectDeferredPromise.resolve = resolve; objectDeferredPromise.reject = reject; }); let objectStreamController = null; const updateUsageCount = (usage) => { this.#usageCount.inputTokens += parseInt(usage?.inputTokens?.toString() ?? "0", 10); this.#usageCount.outputTokens += parseInt(usage?.outputTokens?.toString() ?? "0", 10); this.#usageCount.totalTokens += parseInt(usage?.totalTokens?.toString() ?? "0", 10); this.#usageCount.reasoningTokens += parseInt(usage?.reasoningTokens?.toString() ?? "0", 10); this.#usageCount.cachedInputTokens += parseInt(usage?.cachedInputTokens?.toString() ?? "0", 10); this.#usageCount.cacheCreationInputTokens += parseInt(usage?.cacheCreationInputTokens?.toString() ?? "0", 10); }; super({ start: async (controller) => { try { const stream = await createStream(new WritableStream({ write: (chunk) => { if (chunk.type === "step-output" && chunk.payload?.output?.from === "AGENT" && chunk.payload?.output?.type === "finish" || chunk.type === "step-output" && chunk.payload?.output?.from === "WORKFLOW" && chunk.payload?.output?.type === "finish") { const output = chunk.payload?.output; if (output && "payload" in output && output.payload) { const finishPayload = output.payload; if ("usage" in finishPayload && finishPayload.usage) updateUsageCount(finishPayload.usage); else if ("output" in finishPayload && finishPayload.output) { const outputPayload = finishPayload.output; if ("usage" in outputPayload && outputPayload.usage) updateUsageCount(outputPayload.usage); } } } controller.enqueue(chunk); } })); const getInnerChunk = (chunk) => { if (chunk.type === "workflow-step-output") return getInnerChunk(chunk.payload.output); return chunk; }; let objectResolved = false; for await (const chunk of stream) if (chunk.type === "workflow-step-output") { const innerChunk = getInnerChunk(chunk); if (innerChunk.type === "routing-agent-end" || innerChunk.type === "agent-execution-end" || innerChunk.type === "workflow-execution-end") { if (innerChunk.payload?.usage) updateUsageCount(innerChunk.payload.usage); } if (innerChunk.type === "network-object") { if (objectStreamController) objectStreamController.enqueue(innerChunk.payload?.object); controller.enqueue(innerChunk); } else if (innerChunk.type === "network-object-result") { if (!objectResolved) { objectResolved = true; objectDeferredPromise.resolve(innerChunk.payload?.object); if (objectStreamController) objectStreamController.close(); } controller.enqueue(innerChunk); } else if (innerChunk.type === "network-execution-event-finish") { const finishPayload = { ...innerChunk.payload, usage: this.#usageCount }; controller.enqueue({ ...innerChunk, payload: finishPayload }); } else controller.enqueue(innerChunk); } if (!objectResolved) { objectDeferredPromise.resolve(void 0); if (objectStreamController) objectStreamController.close(); } controller.close(); deferredPromise.resolve(); } catch (error) { controller.error(error); deferredPromise.reject(error); objectDeferredPromise.reject(error); if (objectStreamController) objectStreamController.error(error); } } }); this.#run = run; this.#streamPromise = deferredPromise; this.runId = run.runId; this.#objectPromise = objectDeferredPromise; this.#objectStream = new ReadableStream$1({ start: (ctrl) => { objectStreamController = ctrl; this.#objectStreamController = ctrl; } }); } get status() { return this.#streamPromise.promise.then(() => this.#run._getExecutionResults()).then((res) => res.status); } get result() { return this.#streamPromise.promise.then(() => this.#run._getExecutionResults()); } get usage() { return this.#streamPromise.promise.then(() => this.#usageCount); } /** * Returns a promise that resolves to the structured output object. * Only available when structuredOutput option is provided to network(). * Resolves to undefined if no structuredOutput was requested. */ get object() { return this.#objectPromise.promise; } /** * Returns a ReadableStream of partial objects during structured output generation. * Useful for streaming partial results as they're being generated. */ get objectStream() { return this.#objectStream; } }; //#endregion //#region src/stream/aisdk/v5/transform.ts /** * Sanitizes tool-call input strings for safe JSON parsing. * * LLMs sometimes append internal tokens like `<|call|>`, `<|endoftext|>`, or `<|end|>` * to otherwise valid JSON in streamed tool-call arguments, causing JSON.parse to fail. * * This function first attempts JSON.parse on the original input. If parsing succeeds, * the original string is returned unchanged — this avoids corrupting valid JSON payloads * that legitimately contain `<|...|>` patterns inside string values. * * Only when the original input is not valid JSON does the function fall back to stripping * `<|...|>` token patterns and surrounding whitespace via regex. * * @see https://github.com/mastra-ai/mastra/issues/13261 * @see https://github.com/mastra-ai/mastra/issues/13185 */ function sanitizeToolCallInput(input) { try { JSON.parse(input); return input; } catch { return input.replace(/<\|[^|]*\|>\s*/g, "").trim(); } } /** * Attempts to repair common JSON malformations produced by LLM providers. * * Some LLM providers (e.g., Kimi/K2) occasionally generate malformed JSON for * tool call arguments. This function applies a sequence of targeted fixes for * the most common errors before giving up. * * Repairs applied (in order): * 1. Missing quote before property name: `{"a":"b",c":"d"}` → `{"a":"b","c":"d"}` * 2. Unquoted property names: `{command:"value"}` → `{"command":"value"}` * 3. Single quotes → double quotes (only outside already-double-quoted strings) * 4. Trailing commas: `{"a":1,}` → `{"a":1}` * * @returns The parsed object if repair succeeds, or null if the JSON is unrecoverable. * @see https://github.com/mastra-ai/mastra/issues/11078 */ function tryRepairJson(input) { let repaired = input.trim(); repaired = repaired.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)"/g, (match, prefix, name) => { if (prefix.trimEnd().endsWith("\"")) return match; return `${prefix}"${name}"`; }); repaired = repaired.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, "$1\"$2\":"); repaired = repaired.replace(/'/g, "\""); repaired = repaired.replace(/,(\s*[}\]])/g, "$1"); repaired = repaired.replace(/:\s*(\d{4}-\d{2}-\d{2}(?:T[\d:]+)?)\s*([,}])/g, ": \"$1\"$2"); try { return JSON.parse(repaired); } catch { return null; } } function convertFullStreamChunkToMastra(value, ctx) { switch (value.type) { case "response-metadata": return { type: "response-metadata", runId: ctx.runId, from: "AGENT", payload: { ...value } }; case "text-start": return { type: "text-start", runId: ctx.runId, from: "AGENT", payload: { id: value.id, providerMetadata: value.providerMetadata } }; case "text-delta": if (value.delta || value.providerMetadata != null) return { type: "text-delta", runId: ctx.runId, from: "AGENT", payload: { id: value.id, providerMetadata: value.providerMetadata, text: value.delta } }; return; case "text-end": return { type: "text-end", runId: ctx.runId, from: "AGENT", payload: value }; case "reasoning-start": return { type: "reasoning-start", runId: ctx.runId, from: "AGENT", payload: { id: value.id, providerMetadata: value.providerMetadata } }; case "reasoning-delta": return { type: "reasoning-delta", runId: ctx.runId, from: "AGENT", payload: { id: value.id, providerMetadata: value.providerMetadata, text: value.delta } }; case "reasoning-end": return { type: "reasoning-end", runId: ctx.runId, from: "AGENT", payload: { id: value.id, providerMetadata: value.providerMetadata } }; case "source": return { type: "source", runId: ctx.runId, from: "AGENT", payload: { id: value.id, sourceType: value.sourceType, title: value.title || "", mimeType: value.sourceType === "document" ? value.mediaType : void 0, filename: value.sourceType === "document" ? value.filename : void 0, url: value.sourceType === "url" ? value.url : void 0, providerMetadata: value.providerMetadata } }; case "file": { const pm = value.providerMetadata; return { type: "file", runId: ctx.runId, from: "AGENT", payload: { data: value.data, base64: typeof value.data === "string" && !isUrlString(value.data) ? value.data : void 0, mimeType: value.mediaType, ...pm != null ? { providerMetadata: pm } : {} } }; } case "tool-call": { let toolCallInput = void 0; if (value.input) { const sanitized = sanitizeToolCallInput(value.input); if (sanitized) try { toolCallInput = JSON.parse(sanitized); } catch { const repaired = tryRepairJson(sanitized); if (repaired) toolCallInput = repaired; else { console.error("Error converting tool call input to JSON", { input: value.input }); toolCallInput = void 0; } } } return { type: "tool-call", runId: ctx.runId, from: "AGENT", payload: { toolCallId: value.toolCallId, toolName: value.toolName, args: toolCallInput, providerExecuted: value.providerExecuted, providerMetadata: value.providerMetadata, ...value.observability ? { observability: value.observability } : {} } }; } case "tool-result": return { type: "tool-result", runId: ctx.runId, from: "AGENT", payload: { toolCallId: value.toolCallId, toolName: value.toolName, result: value.result, isError: value.isError, providerExecuted: value.providerExecuted, providerMetadata: value.providerMetadata } }; case "tool-input-start": return { type: "tool-call-input-streaming-start", runId: ctx.runId, from: "AGENT", payload: { toolCallId: value.id, toolName: value.toolName, providerExecuted: value.providerExecuted, providerMetadata: value.providerMetadata, dynamic: value.dynamic, ...value.observability ? { observability: value.observability } : {} } }; case "tool-input-delta": if (value.delta) return { type: "tool-call-delta", runId: ctx.runId, from: "AGENT", payload: { argsTextDelta: value.delta, toolCallId: value.id, providerMetadata: value.providerMetadata } }; return; case "tool-input-end": return { type: "tool-call-input-streaming-end", runId: ctx.runId, from: "AGENT", payload: { toolCallId: value.id, providerMetadata: value.providerMetadata } }; case "finish": const { finishReason, usage, providerMetadata, messages, ...rest } = value; return { type: "finish", runId: ctx.runId, from: "AGENT", payload: { providerMetadata: value.providerMetadata, stepResult: { reason: normalizeFinishReason(value.finishReason) }, output: { usage: normalizeUsage(value.usage) }, metadata: { providerMetadata: value.providerMetadata }, messages: messages ?? { all: [], user: [], nonUser: [] }, ...rest } }; case "error": return { type: "error", runId: ctx.runId, from: "AGENT", payload: value }; case "raw": return { type: "raw", runId: ctx.runId, from: "AGENT", payload: value.rawValue }; } } function convertMastraChunkToAISDKv5({ chunk, mode = "stream" }) { switch (chunk.type) { case "start": return { type: "start" }; case "step-start": const { messageId: _messageId, ...rest } = chunk.payload; return { type: "start-step", request: rest.request, warnings: rest.warnings || [] }; case "raw": return { type: "raw", rawValue: chunk.payload }; case "finish": return { type: "finish", finishReason: chunk.payload.stepResult.reason, totalUsage: chunk.payload.output.usage }; case "reasoning-start": return { type: "reasoning-start", id: chunk.payload.id, providerMetadata: chunk.payload.providerMetadata }; case "reasoning-delta": return { type: "reasoning-delta", id: chunk.payload.id, text: chunk.payload.text, providerMetadata: chunk.payload.providerMetadata }; case "reasoning-signature": throw new Error("AISDKv5 chunk type \"reasoning-signature\" not supported"); case "redacted-reasoning": throw new Error("AISDKv5 chunk type \"redacted-reasoning\" not supported"); case "reasoning-end": return { type: "reasoning-end", id: chunk.payload.id, providerMetadata: chunk.payload.providerMetadata }; case "source": if (chunk.payload.sourceType === "url") return { type: "source", sourceType: "url", id: chunk.payload.id, url: chunk.payload.url, title: chunk.payload.title, providerMetadata: chunk.payload.providerMetadata }; else return { type: "source", sourceType: "document", id: chunk.payload.id, mediaType: chunk.payload.mimeType, title: chunk.payload.title, filename: chunk.payload.filename, providerMetadata: chunk.payload.providerMetadata }; case "file": { const filePart = mode === "generate" ? { type: "file", file: new DefaultGeneratedFile({ data: chunk.payload.data, mediaType: chunk.payload.mimeType }) } : { type: "file", file: new DefaultGeneratedFileWithType({ data: chunk.payload.data, mediaType: chunk.payload.mimeType }) }; if (chunk.payload.providerMetadata) filePart.providerMetadata = chunk.payload.providerMetadata; return filePart; } case "tool-call": { const toolCallPart = { type: "tool-call", toolCallId: chunk.payload.toolCallId, providerMetadata: chunk.payload.providerMetadata, providerExecuted: chunk.payload.providerExecuted, toolName: chunk.payload.toolName, input: chunk.payload.args }; if (chunk.payload.observability) toolCallPart.observability = chunk.payload.observability; return toolCallPart; } case "tool-call-input-streaming-start": return { type: "tool-input-start", id: chunk.payload.toolCallId, toolName: chunk.payload.toolName, dynamic: !!chunk.payload.dynamic, providerMetadata: chunk.payload.providerMetadata, providerExecuted: chunk.payload.providerExecuted, ...chunk.payload.observability ? { observability: chunk.payload.observability } : {} }; case "tool-call-input-streaming-end": return { type: "tool-input-end", id: chunk.payload.toolCallId, providerMetadata: chunk.payload.providerMetadata }; case "tool-call-delta": return { type: "tool-input-delta", id: chunk.payload.toolCallId, delta: chunk.payload.argsTextDelta, providerMetadata: chunk.payload.providerMetadata }; case "step-finish": { const { request: _request, providerMetadata: metadataProviderMetadata, ...rest } = chunk.payload.metadata; return { type: "finish-step", response: { id: chunk.payload.id || "", timestamp: /* @__PURE__ */ new Date(), modelId: rest.modelId || "", ...rest }, usage: chunk.payload.output.usage, finishReason: chunk.payload.stepResult.reason, providerMetadata: metadataProviderMetadata ?? chunk.payload.providerMetadata }; } case "text-delta": return { type: "text-delta", id: chunk.payload.id, text: chunk.payload.text, providerMetadata: chunk.payload.providerMetadata }; case "text-end": return { type: "text-end", id: chunk.payload.id, providerMetadata: chunk.payload.providerMetadata }; case "text-start": return { type: "text-start", id: chunk.payload.id, providerMetadata: chunk.payload.providerMetadata }; case "tool-result": return { type: "tool-result", input: chunk.payload.args, toolCallId: chunk.payload.toolCallId, providerExecuted: chunk.payload.providerExecuted, toolName: chunk.payload.toolName, output: chunk.payload.result }; case "tool-error": return { type: "tool-error", error: chunk.payload.error, input: chunk.payload.args, toolCallId: chunk.payload.toolCallId, providerExecuted: chunk.payload.providerExecuted, toolName: chunk.payload.toolName }; case "abort": return { type: "abort" }; case "error": return { type: "error", error: chunk.payload.error }; case "object": return { type: "object", object: chunk.object }; default: if (chunk.type && "payload" in chunk && chunk.payload) return { type: chunk.type, ...chunk.payload || {} }; return; } } /** * Type guard to check if usage is in V3 format (nested objects) */ function isV3Usage(usage) { if (!usage || typeof usage !== "object") return false; const u = usage; return typeof u.inputTokens === "object" && u.inputTokens !== null && "total" in u.inputTokens && typeof u.outputTokens === "object" && u.outputTokens !== null && "total" in u.outputTokens; } /** * Normalizes usage from either V2 (flat) or V3 (nested) format to Mastra's flat format. * V2 format: { inputTokens: number, outputTokens: number, totalTokens?: number } * V3 format: { inputTokens: { total, noCache, cacheRead, cacheWrite }, outputTokens: { total, text, reasoning } } * * The original usage data is preserved in the `raw` field for advanced use cases. */ function normalizeUsage(usage) { if (!usage) return { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0, reasoningTokens: void 0, cachedInputTokens: void 0, cacheCreationInputTokens: void 0, raw: void 0 }; if (isV3Usage(usage)) { const inputTokens = usage.inputTokens.total; const outputTokens = usage.outputTokens.total; return { inputTokens, outputTokens, totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0), reasoningTokens: usage.outputTokens.reasoning, cachedInputTokens: usage.inputTokens.cacheRead, cacheCreationInputTokens: usage.inputTokens.cacheWrite, raw: usage }; } const v2Usage = usage; return { inputTokens: v2Usage.inputTokens, outputTokens: v2Usage.outputTokens, totalTokens: v2Usage.totalTokens ?? (v2Usage.inputTokens ?? 0) + (v2Usage.outputTokens ?? 0), reasoningTokens: v2Usage.reasoningTokens, cachedInputTokens: v2Usage.cachedInputTokens, cacheCreationInputTokens: v2Usage.cacheCreationInputTokens, raw: usage }; } /** * Type guard to check if a finish reason is V3 format (object with unified/raw properties) */ function isV3FinishReason(finishReason) { return typeof finishReason === "object" && finishReason !== null && "unified" in finishReason; } /** * Normalize finish reason from either V2/V5 (string) or V3/V6 (object) format to a string. * * V2/V5 format: 'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other' | 'unknown' * V3/V6 format: { unified: 'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other', raw: string | undefined } * * We normalize to the unified string value for internal Mastra use. * Note: V6 removed 'unknown' and merged it into 'other'. */ function normalizeFinishReason(finishReason) { if (!finishReason) return "other"; if (finishReason === "tripwire" || finishReason === "retry") return finishReason; if (isV3FinishReason(finishReason)) return finishReason.unified; return finishReason === "unknown" ? "other" : finishReason; } //#endregion //#region src/stream/caching-transform-stream.ts /** * Creates a TransformStream that caches all chunks passing through it. * * Use this for workflow streaming where you need resumable streams * without changing to a PubSub-based architecture. * * @example * ```typescript * const cache = mastra.getServerCache(); * const { transform, getHistory } = createCachingTransformStream({ * cache, * cacheKey: runId, * }); * * // Use the transform stream * const cachedStream = sourceStream.pipeThrough(transform); * * // Later, get cached history for replay * const history = await getHistory(); * ``` */ function createCachingTransformStream(options) { const { cache, cacheKey, serialize = (x) => x, deserialize = (x) => x } = options; const transform = new TransformStream({ transform(chunk, controller) { const serialized = serialize(chunk); cache.listPush(cacheKey, serialized).catch(() => {}); controller.enqueue(chunk); } }); const getHistory = async (offset = 0) => { return (await cache.listFromTo(cacheKey, offset)).map((item) => deserialize(item)); }; const clearCache = async () => { await cache.delete(cacheKey); }; return { transform, getHistory, clearCache }; } /** * Creates a ReadableStream that first emits cached history, then pipes from a live source. * * Use this when a client reconnects and needs to receive missed chunks * before continuing with the live stream. * * @example * ```typescript * const cache = mastra.getServerCache(); * * // Get cached history * const history = await cache.listFromTo(runId, 0); * * // Create combined stream * const stream = createReplayStream({ * history: history as ChunkType[], * liveSource: workflow.stream(), * cache, * cacheKey: runId, * }); * ``` */ function createReplayStream(options) { const { history, liveSource, cache, cacheKey, serialize = (x) => x } = options; let historyIndex = 0; let liveReader = null; let historyComplete = false; return new ReadableStream({ async pull(controller) { if (!historyComplete) { if (historyIndex < history.length) { controller.enqueue(history[historyIndex]); historyIndex++; return; } historyComplete = true; liveReader = liveSource.getReader(); } if (liveReader) try { const { done, value } = await liveReader.read(); if (done) { controller.close(); return; } if (cache && cacheKey) { const serialized = serialize(value); cache.listPush(cacheKey, serialized).catch(() => {}); } controller.enqueue(value); } catch (error) { controller.error(error); } }, cancel() { if (liveReader) liveReader.cancel(); } }); } /** * Helper to create a caching transform and get history in one call. * * This is the recommended way to add caching to workflow streams. * * @example * ```typescript * const { pipeThrough, getHistory, clearCache } = withStreamCaching({ * cache: mastra.getServerCache(), * cacheKey: runId, * }); * * // Apply caching to a stream * const cachedStream = workflow.fullStream.pipeThrough(pipeThrough()); * * // On reconnect, get history and create replay stream * const history = await getHistory(); * const replayStream = createReplayStream({ * history, * liveSource: workflow.resumeStream(), * }); * ``` */ function withStreamCaching(options) { const { cache, cacheKey, serialize, deserialize } = options; return { pipeThrough: () => { const { transform } = createCachingTransformStream({ cache, cacheKey, serialize, deserialize }); return transform; }, getHistory: async (offset = 0) => { const deserializeFn = deserialize ?? ((x) => x); return (await cache.listFromTo(cacheKey, offset)).map((item) => deserializeFn(item)); }, clearCache: async () => { await cache.delete(cacheKey); } }; } //#endregion //#region src/stream/smooth-stream.ts const CHUNKING_PATTERNS = { word: /\S+\s+/m, line: /\n+/m }; const wait = (delayInMs) => new Promise((resolve) => setTimeout(resolve, delayInMs)); function createChunkDetector(chunking) { if (typeof chunking === "object" && "segment" in chunking && typeof chunking.segment === "function") return (buffer) => { if (!buffer) return null; return chunking.segment(buffer)[Symbol.iterator]().next().value?.segment || null; }; if (typeof chunking === "function") return (buffer) => { const match = chunking(buffer); if (match == null) return null; if (!match.length) throw new TypeError("The chunking function must return a non-empty string."); if (!buffer.startsWith(match)) throw new TypeError("The chunking function must return a prefix of the buffered text."); return match; }; const pattern = typeof chunking === "string" ? CHUNKING_PATTERNS[chunking] : chunking; if (!(pattern instanceof RegExp)) throw new TypeError("chunking must be \"word\", \"line\", a RegExp, an Intl.Segmenter, or a chunk detector function."); return (buffer) => { pattern.lastIndex = 0; const match = pattern.exec(buffer); if (!match) return null; const detected = buffer.slice(0, match.index) + match[0]; if (!detected.length) throw new TypeError("The chunking RegExp must match a non-empty string."); return detected; }; } /** * Creates a transform stream that buffers text and reasoning deltas and emits * them in consistent, delayed chunks. Other stream parts pass through without * modification after any buffered content has been emitted. * * @experimental This API may change in a future release. */ function smoothStream({ delayInMs = 10, chunking = "word" } = {}) { const detectChunk = createChunkDetector(chunking); let buffer = ""; let bufferedChunk; let bufferedMetadata; let bufferedProviderMetadata; const enqueueBufferedText = (controller, text) => { if (!bufferedChunk || !text && !(bufferedMetadata !== void 0 || bufferedProviderMetadata !== void 0)) return; const { metadata: _metadata, ...chunkWithoutMetadata } = bufferedChunk; const { providerMetadata: _providerMetadata, ...payloadWithoutProviderMetadata } = bufferedChunk.payload; controller.enqueue({ ...chunkWithoutMetadata, ...bufferedMetadata !== void 0 ? { metadata: bufferedMetadata } : {}, payload: { ...payloadWithoutProviderMetadata, ...bufferedProviderMetadata !== void 0 ? { providerMetadata: bufferedProviderMetadata } : {}, text } }); bufferedMetadata = void 0; bufferedProviderMetadata = void 0; }; const flushBuffer = (controller) => { enqueueBufferedText(controller, buffer); buffer = ""; bufferedChunk = void 0; bufferedMetadata = void 0; bufferedProviderMetadata = void 0; }; return new TransformStream$1({ async transform(chunk, controller) { if (chunk.type !== "text-delta" && chunk.type !== "reasoning-delta") { flushBuffer(controller); controller.enqueue(chunk); return; } if (bufferedChunk && (chunk.type !== bufferedChunk.type || chunk.payload.id !== bufferedChunk.payload.id)) flushBuffer(controller); buffer += chunk.payload.text; bufferedChunk = chunk; bufferedMetadata = chunk.metadata ?? bufferedMetadata; bufferedProviderMetadata = chunk.payload.providerMetadata ?? bufferedProviderMetadata; let match; while ((match = detectChunk(buffer)) != null) { enqueueBufferedText(controller, match); buffer = buffer.slice(match.length); if (delayInMs !== null) await wait(delayInMs); } }, flush(controller) { flushBuffer(controller); } }); } //#endregion export { convertFullStreamChunkToMastra as a, WorkflowRunOutput as c, withStreamCaching as i, convertFullStreamChunkToUIMessageStream as l, createCachingTransformStream as n, convertMastraChunkToAISDKv5 as o, createReplayStream as r, MastraAgentNetworkStream as s, smoothStream as t }; //# sourceMappingURL=stream-CRtv-q6z.js.map