@mastra/core
Version:
1,350 lines (1,344 loc) • 155 kB
JavaScript
'use strict';
var chunk7ISY3K4R_cjs = require('./chunk-7ISY3K4R.cjs');
var chunk2LBT6OZS_cjs = require('./chunk-2LBT6OZS.cjs');
var chunkXB4FLS7A_cjs = require('./chunk-XB4FLS7A.cjs');
var chunkYGTB7A25_cjs = require('./chunk-YGTB7A25.cjs');
var chunkLP4WZA6D_cjs = require('./chunk-LP4WZA6D.cjs');
var chunkWSD4JNMB_cjs = require('./chunk-WSD4JNMB.cjs');
var chunkRWGBZBPV_cjs = require('./chunk-RWGBZBPV.cjs');
var chunkZQADKFIL_cjs = require('./chunk-ZQADKFIL.cjs');
var chunkXSOONORA_cjs = require('./chunk-XSOONORA.cjs');
var crypto = require('crypto');
var web = require('stream/web');
var events = require('events');
var schemaCompat = require('@mastra/schema-compat');
// src/stream/aisdk/v5/compat/delayed-promise.ts
var DelayedPromise = class {
status = {
type: "pending"
};
_promise;
_resolve = void 0;
_reject = void 0;
get promise() {
if (this._promise) {
return this._promise;
}
this._promise = new Promise((resolve, reject) => {
if (this.status.type === "resolved") {
resolve(this.status.value);
} else if (this.status.type === "rejected") {
reject(this.status.error);
}
this._resolve = resolve;
this._reject = reject;
});
return this._promise;
}
resolve(value) {
this.status = { type: "resolved", value };
if (this._promise) {
this._resolve?.(value);
}
}
reject(error) {
this.status = { type: "rejected", error };
if (this._promise) {
this._reject?.(error);
}
}
};
// src/stream/aisdk/v5/compat/consume-stream.ts
async function consumeStream({
stream,
onError,
logger
}) {
const reader = stream.getReader();
try {
while (true) {
const { done } = await reader.read();
if (done) break;
}
} catch (error) {
logger?.error("consumeStream error", error);
onError?.(error);
} finally {
reader.releaseLock();
}
}
// src/stream/base/input.ts
function safeEnqueue(controller, chunk) {
try {
controller.enqueue(chunk);
return true;
} catch {
return false;
}
}
function safeClose(controller) {
try {
controller.close();
return true;
} catch {
return false;
}
}
function safeError(controller, error) {
try {
controller.error(error);
return true;
} catch {
return false;
}
}
var MastraModelInput = class extends chunkWSD4JNMB_cjs.MastraBase {
initialize({ runId, createStream, onResult }) {
const self = this;
let outputStream;
outputStream = new ReadableStream({
async start(controller) {
try {
const stream = await createStream();
chunk2LBT6OZS_cjs.attachModelStreamTransport(outputStream, chunk2LBT6OZS_cjs.readModelStreamTransport(stream));
const initialChunks = onResult({
warnings: stream.warnings,
request: stream.request,
rawResponse: stream.rawResponse || stream.response || {}
});
if (initialChunks) {
for (const chunk of Array.isArray(initialChunks) ? initialChunks : [initialChunks]) {
controller.enqueue(chunk);
}
}
await self.transform({
runId,
stream: stream.stream,
controller
});
safeClose(controller);
} catch (error) {
safeError(controller, error);
}
}
});
return outputStream;
}
};
function asJsonSchema(schema) {
if (!schema) {
return void 0;
}
if (chunkXB4FLS7A_cjs.isStandardSchemaWithJSON(schema)) {
const jsonSchema = chunkXB4FLS7A_cjs.standardSchemaToJSONSchema(schema, { io: "input", target: "draft-07" });
return jsonSchema;
}
return schema;
}
function getTransformedSchema(schema, options) {
if (!schema) {
return void 0;
}
const jsonSchema = options?.model ? schemaCompat.applyCompatLayer({
schema,
compatLayers: [new schemaCompat.AnthropicSchemaCompatLayer(options.model)],
mode: "jsonSchema"
}) : asJsonSchema(schema);
if (!jsonSchema) {
return void 0;
}
const { $schema, ...itemSchema } = jsonSchema;
if (itemSchema.type === "array") {
const innerElement = itemSchema.items;
const arrayOutputSchema = {
$schema,
type: "object",
properties: {
elements: { type: "array", items: innerElement }
},
required: ["elements"],
additionalProperties: false
};
return {
jsonSchema: arrayOutputSchema,
outputFormat: "array"
};
}
if (itemSchema.enum && Array.isArray(itemSchema.enum)) {
const enumOutputSchema = {
$schema,
type: "object",
properties: {
result: { type: itemSchema.type || "string", enum: itemSchema.enum }
},
required: ["result"],
additionalProperties: false
};
return {
jsonSchema: enumOutputSchema,
outputFormat: "enum"
};
}
return {
jsonSchema,
outputFormat: jsonSchema.type
// 'object'
};
}
function getResponseFormat(schema, options) {
if (schema) {
const transformedSchema = getTransformedSchema(schema, options);
return {
type: "json",
schema: transformedSchema?.jsonSchema
};
}
return {
type: "text"
};
}
// src/stream/base/output-format-handlers.ts
function escapeUnescapedControlCharsInJsonStrings(text) {
let result = "";
let inString = false;
let i = 0;
while (i < text.length) {
const char = text[i];
if (char === "\\" && i + 1 < text.length) {
result += char + text[i + 1];
i += 2;
continue;
}
if (char === '"') {
inString = !inString;
result += char;
i++;
continue;
}
if (inString) {
if (char === "\n") {
result += "\\n";
i++;
continue;
}
if (char === "\r") {
result += "\\r";
i++;
continue;
}
if (char === " ") {
result += "\\t";
i++;
continue;
}
}
result += char;
i++;
}
return result;
}
var BaseFormatHandler = class {
/**
* The original user-provided schema (Zod, JSON Schema, or AI SDK Schema).
*/
schema;
/**
* Validate partial chunks as they are streamed. @planned
*/
validatePartialChunks = false;
partialSchema;
constructor(schema, options = {}) {
this.schema = schema;
if (options.validatePartialChunks && this.isZodSchema(schema) && "partial" in schema && typeof schema.partial === "function") {
this.partialSchema = schema.partial();
this.validatePartialChunks = true;
}
}
/**
* Checks if the original schema is a Zod schema with safeParse method.
*/
isZodSchema(schema) {
return schemaCompat.isZodType(schema);
}
/**
* Validates a value against the schema using StandardSchemaWithJSON's validate method.
*/
async validateValue(value) {
if (!this.schema) {
return {
success: true,
value
};
}
if (this.isZodSchema(this.schema)) {
try {
const ssResult = await this.schema["~standard"].validate(value);
if (!ssResult.issues) {
return {
success: true,
value: ssResult.value
};
}
const errorMessages = ssResult.issues.map((e) => `- ${e.path?.join(".") || "root"}: ${e.message}`).join("\n");
const zodResult = this.schema.safeParse(value);
const zodError = !zodResult.success ? zodResult.error : void 0;
return {
success: false,
error: new chunkXSOONORA_cjs.MastraError(
{
domain: chunkXSOONORA_cjs.ErrorDomain.AGENT,
category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM,
id: "STRUCTURED_OUTPUT_SCHEMA_VALIDATION_FAILED",
text: `Structured output validation failed: ${errorMessages}`,
details: {
value: typeof value === "object" ? JSON.stringify(value) : String(value)
}
},
zodError
)
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error("Zod validation failed", { cause: error })
};
}
}
try {
const ssResult = await this.schema["~standard"].validate(value);
if (!ssResult.issues) {
return {
success: true,
value: ssResult.value
};
}
const errorMessages = ssResult.issues.map((e) => `- ${e.path?.join(".") || "root"}: ${e.message}`).join("\n");
return {
success: false,
error: new chunkXSOONORA_cjs.MastraError({
domain: chunkXSOONORA_cjs.ErrorDomain.AGENT,
category: chunkXSOONORA_cjs.ErrorCategory.SYSTEM,
id: "STRUCTURED_OUTPUT_SCHEMA_VALIDATION_FAILED",
text: `Structured output validation failed: ${errorMessages}`,
details: {
value: typeof value === "object" ? JSON.stringify(value) : String(value)
}
})
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error("Validation failed", { cause: error })
};
}
}
/**
* Preprocesses accumulated text to handle LLMs that wrap JSON in code blocks
* and fix common JSON formatting issues like unescaped newlines in strings.
* Extracts content from the first complete valid ```json...``` code block or removes opening ```json prefix if no complete code block is found (streaming chunks).
* @param accumulatedText - Raw accumulated text from streaming
* @returns Processed text ready for JSON parsing
*/
preprocessText(accumulatedText) {
let processedText = accumulatedText;
if (processedText.includes("<|message|>")) {
const match = processedText.match(/<\|message\|>([\s\S]+)$/);
if (match && match[1]) {
processedText = match[1];
}
}
const trimmedStart = processedText.trimStart();
if (/^```json\b/.test(trimmedStart)) {
const match = trimmedStart.match(/^```json\s*\n?([\s\S]*?)\n?\s*```\s*$/);
if (match && match[1]) {
processedText = match[1].trim();
} else {
processedText = trimmedStart.replace(/^```json\s*\n?/, "");
}
}
processedText = escapeUnescapedControlCharsInJsonStrings(processedText);
return processedText;
}
};
var ObjectFormatHandler = class extends BaseFormatHandler {
type = "object";
async processPartialChunk({
accumulatedText,
previousObject
}) {
const processedAccumulatedText = this.preprocessText(accumulatedText);
const { value: currentObjectJson, state } = await chunkZQADKFIL_cjs.parsePartialJson(processedAccumulatedText);
if (this.validatePartialChunks && this.partialSchema) {
const result = this.partialSchema?.safeParse(currentObjectJson);
if (result.success && result.data && result.data !== void 0 && !chunkZQADKFIL_cjs.isDeepEqualData(previousObject, result.data)) {
return {
shouldEmit: true,
emitValue: result.data,
newPreviousResult: result.data
};
}
return { shouldEmit: false };
}
if (currentObjectJson !== void 0 && currentObjectJson !== null && typeof currentObjectJson === "object" && !chunkZQADKFIL_cjs.isDeepEqualData(previousObject, currentObjectJson)) {
return {
shouldEmit: ["successful-parse", "repaired-parse"].includes(state),
emitValue: currentObjectJson,
newPreviousResult: currentObjectJson
};
}
return { shouldEmit: false };
}
async validateAndTransformFinal(finalRawValue) {
if (!finalRawValue) {
return {
success: false,
error: new Error("No object generated: could not parse the response.")
};
}
const rawValue = this.preprocessText(finalRawValue);
const { value } = await chunkZQADKFIL_cjs.parsePartialJson(rawValue);
return this.validateValue(value);
}
};
var ArrayFormatHandler = class extends BaseFormatHandler {
type = "array";
/** Previously filtered array to track changes */
textPreviousFilteredArray = [];
/** Whether we've emitted the initial empty array */
hasEmittedInitialArray = false;
async processPartialChunk({
accumulatedText,
previousObject
}) {
const processedAccumulatedText = this.preprocessText(accumulatedText);
const { value: currentObjectJson, state: parseState } = await chunkZQADKFIL_cjs.parsePartialJson(processedAccumulatedText);
if (currentObjectJson !== void 0 && !chunkZQADKFIL_cjs.isDeepEqualData(previousObject, currentObjectJson)) {
const rawElements = currentObjectJson && typeof currentObjectJson === "object" && "elements" in currentObjectJson && Array.isArray(currentObjectJson.elements) ? currentObjectJson.elements : [];
const filteredElements = [];
for (let i = 0; i < rawElements.length; i++) {
const element = rawElements[i];
if (i === rawElements.length - 1 && parseState !== "successful-parse") {
if (element && typeof element === "object" && Object.keys(element).length > 0) {
filteredElements.push(element);
}
} else {
if (element && typeof element === "object" && Object.keys(element).length > 0) {
filteredElements.push(element);
}
}
}
if (!this.hasEmittedInitialArray) {
this.hasEmittedInitialArray = true;
if (filteredElements.length === 0) {
this.textPreviousFilteredArray = [];
return {
shouldEmit: true,
emitValue: [],
newPreviousResult: currentObjectJson
};
}
}
if (!chunkZQADKFIL_cjs.isDeepEqualData(this.textPreviousFilteredArray, filteredElements)) {
this.textPreviousFilteredArray = [...filteredElements];
return {
shouldEmit: true,
emitValue: filteredElements,
newPreviousResult: currentObjectJson
};
}
}
return { shouldEmit: false };
}
async validateAndTransformFinal(_finalValue) {
const resultValue = this.textPreviousFilteredArray;
if (!resultValue) {
return {
success: false,
error: new Error("No object generated: could not parse the response.")
};
}
return this.validateValue(resultValue);
}
};
var EnumFormatHandler = class extends BaseFormatHandler {
type = "enum";
/** Previously emitted enum result to avoid duplicate emissions */
textPreviousEnumResult;
/**
* Finds the best matching enum value for a partial result string.
* If multiple values match, returns the partial string. If only one matches, returns that value.
* @param partialResult - Partial enum string from streaming
* @returns Best matching enum value or undefined if no matches
*/
findBestEnumMatch(partialResult) {
if (!this.schema) {
return void 0;
}
const outputJsonSchema = chunkXB4FLS7A_cjs.standardSchemaToJSONSchema(this.schema);
const enumValues = outputJsonSchema?.enum;
if (!enumValues) {
return void 0;
}
const possibleEnumValues = enumValues.filter((value) => typeof value === "string").filter((enumValue) => enumValue.startsWith(partialResult));
if (possibleEnumValues.length === 0) {
return void 0;
}
const firstMatch = possibleEnumValues[0];
return possibleEnumValues.length === 1 && firstMatch !== void 0 ? firstMatch : partialResult;
}
async processPartialChunk({
accumulatedText,
previousObject
}) {
const processedAccumulatedText = this.preprocessText(accumulatedText);
const { value: currentObjectJson } = await chunkZQADKFIL_cjs.parsePartialJson(processedAccumulatedText);
if (currentObjectJson !== void 0 && currentObjectJson !== null && typeof currentObjectJson === "object" && !Array.isArray(currentObjectJson) && "result" in currentObjectJson && typeof currentObjectJson.result === "string" && !chunkZQADKFIL_cjs.isDeepEqualData(previousObject, currentObjectJson)) {
const partialResult = currentObjectJson.result;
const bestMatch = this.findBestEnumMatch(partialResult);
if (partialResult.length > 0 && bestMatch && bestMatch !== this.textPreviousEnumResult) {
this.textPreviousEnumResult = bestMatch;
return {
shouldEmit: true,
emitValue: bestMatch,
newPreviousResult: currentObjectJson
};
}
}
return { shouldEmit: false };
}
async validateAndTransformFinal(rawFinalValue) {
const processedValue = this.preprocessText(rawFinalValue);
const { value } = await chunkZQADKFIL_cjs.parsePartialJson(processedValue);
if (!(typeof value === "object" && value !== null && "result" in value)) {
return {
success: false,
error: new Error("Invalid enum format: expected object with result property")
};
}
const finalValue = value;
if (!finalValue || typeof finalValue !== "object" || typeof finalValue.result !== "string") {
return {
success: false,
error: new Error("Invalid enum format: expected object with result property")
};
}
return this.validateValue(finalValue.result);
}
};
function createOutputHandler({ schema }) {
const normalizedSchema = schema ? chunkXB4FLS7A_cjs.toStandardSchema(schema) : void 0;
const transformedSchema = getTransformedSchema(normalizedSchema);
switch (transformedSchema?.outputFormat) {
case "array":
return new ArrayFormatHandler(normalizedSchema);
case "enum":
return new EnumFormatHandler(normalizedSchema);
case "object":
default:
return new ObjectFormatHandler(normalizedSchema);
}
}
function createObjectStreamTransformer({
structuredOutput,
logger
}) {
const handler = createOutputHandler({ schema: structuredOutput?.schema });
let accumulatedText = "";
let previousObject = void 0;
let currentRunId;
let finalResult;
return new web.TransformStream({
async transform(chunk, controller) {
if (chunk.runId) {
currentRunId = chunk.runId;
}
if (chunk.type === "text-delta" && typeof chunk.payload?.text === "string") {
accumulatedText += chunk.payload.text;
const result = await handler.processPartialChunk({
accumulatedText,
previousObject
});
if (result.shouldEmit) {
previousObject = result.newPreviousResult ?? previousObject;
const chunkData = {
from: chunk.from,
runId: chunk.runId,
type: "object",
object: result.emitValue
// TODO: handle partial runtime type validation of json chunks
};
controller.enqueue(chunkData);
}
}
if (chunk.type === "text-end") {
controller.enqueue(chunk);
if (accumulatedText?.trim() && !finalResult) {
finalResult = await handler.validateAndTransformFinal(accumulatedText);
if (finalResult.success) {
controller.enqueue({
from: "AGENT" /* AGENT */,
runId: currentRunId ?? "",
type: "object-result",
object: finalResult.value
});
}
}
return;
}
controller.enqueue(chunk);
},
async flush(controller) {
if (finalResult && !finalResult.success) {
handleValidationError(finalResult.error, controller);
}
if (accumulatedText?.trim() && !finalResult) {
finalResult = await handler.validateAndTransformFinal(accumulatedText);
if (finalResult.success) {
controller.enqueue({
from: "AGENT" /* AGENT */,
runId: currentRunId ?? "",
type: "object-result",
object: finalResult.value
});
} else {
handleValidationError(finalResult.error, controller);
}
}
}
});
function handleValidationError(error, controller) {
if (structuredOutput?.errorStrategy === "warn") {
logger?.warn(error.message);
} else if (structuredOutput?.errorStrategy === "fallback") {
controller.enqueue({
from: "AGENT" /* AGENT */,
runId: currentRunId ?? "",
type: "object-result",
object: structuredOutput.fallbackValue
});
} else {
controller.enqueue({
from: "AGENT" /* AGENT */,
runId: currentRunId ?? "",
type: "error",
payload: {
error
}
});
}
}
}
function createJsonTextStreamTransformer(schema) {
let previousArrayLength = 0;
let hasStartedArray = false;
let chunkCount = 0;
const outputSchema = getTransformedSchema(schema);
return new web.TransformStream({
transform(chunk, controller) {
if (chunk.type !== "object" || !chunk.object) {
return;
}
if (outputSchema?.outputFormat === "array" && Array.isArray(chunk.object)) {
chunkCount++;
if (chunkCount === 1) {
if (chunk.object.length > 0) {
controller.enqueue(JSON.stringify(chunk.object));
previousArrayLength = chunk.object.length;
hasStartedArray = true;
return;
}
}
if (!hasStartedArray) {
controller.enqueue("[");
hasStartedArray = true;
}
for (let i = previousArrayLength; i < chunk.object.length; i++) {
const elementJson = JSON.stringify(chunk.object[i]);
if (i > 0) {
controller.enqueue("," + elementJson);
} else {
controller.enqueue(elementJson);
}
}
previousArrayLength = chunk.object.length;
} else {
controller.enqueue(JSON.stringify(chunk.object));
}
},
flush(controller) {
if (hasStartedArray && outputSchema?.outputFormat === "array" && chunkCount > 1) {
controller.enqueue("]");
}
}
});
}
// src/stream/base/output.ts
var STRUCTURED_OUTPUT_PROCESSOR_NAME = "structured-output";
function createDestructurableOutput(output) {
return new Proxy(output, {
get(target, prop, _receiver) {
const originalValue = Reflect.get(target, prop, target);
if (typeof originalValue === "function") {
return originalValue.bind(target);
}
return originalValue;
}
});
}
var MastraModelOutput = class extends chunkWSD4JNMB_cjs.MastraBase {
#status = "running";
#error;
#baseStream;
#bufferedChunks = [];
#streamFinished = false;
#finishCallbackSent = false;
#emitter = new events.EventEmitter();
#bufferedSteps = [];
#bufferedReasoningDetails = {};
#bufferedByStep = {
text: "",
reasoning: [],
sources: [],
files: [],
toolCalls: [],
toolResults: [],
dynamicToolCalls: [],
dynamicToolResults: [],
staticToolCalls: [],
staticToolResults: [],
content: [],
usage: { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0 },
warnings: [],
request: {},
response: {
id: "",
timestamp: /* @__PURE__ */ new Date(),
modelId: "",
messages: [],
uiMessages: []
},
reasoningText: "",
providerMetadata: void 0,
finishReason: void 0
};
#bufferedText = [];
#bufferedObject;
#bufferedTextChunks = {};
#bufferedSources = [];
#bufferedReasoning = [];
#bufferedFiles = [];
#toolCallArgsDeltas = {};
#toolCallDeltaIdNameMap = {};
#toolCallStreamingMeta = {};
#toolCalls = [];
#toolResults = [];
#warnings = [];
#finishReason = void 0;
/**
* Provider-specific metadata captured from the most recent step `finish`
* chunk (e.g. AWS Bedrock guardrail trace under
* `providerMetadata.bedrock.trace`). Exposed via {@link _getImmediateProviderMetadata}
* so output-step processors can attribute content-filter blocks, for which
* the completed-steps array is empty.
*/
#stepProviderMetadata = void 0;
#request = {};
#usageCount = {
inputTokens: void 0,
outputTokens: void 0,
totalTokens: void 0
};
#tripwire = void 0;
#transportRef;
#transportClosed = false;
#delayedPromises = {
suspendPayload: new DelayedPromise(),
resumeSchema: new DelayedPromise(),
object: new DelayedPromise(),
finishReason: new DelayedPromise(),
usage: new DelayedPromise(),
warnings: new DelayedPromise(),
providerMetadata: new DelayedPromise(),
response: new DelayedPromise(),
request: new DelayedPromise(),
text: new DelayedPromise(),
reasoning: new DelayedPromise(),
reasoningText: new DelayedPromise(),
sources: new DelayedPromise(),
files: new DelayedPromise(),
toolCalls: new DelayedPromise(),
toolResults: new DelayedPromise(),
steps: new DelayedPromise(),
totalUsage: new DelayedPromise(),
content: new DelayedPromise()
};
#consumptionStarted = false;
#consumeStreamPromise;
#consumeStreamErrored = false;
#consumeStreamError;
#returnScorerData = false;
#structuredOutputMode = void 0;
#model;
/**
* Unique identifier for this execution run.
*/
runId;
#options;
/**
* The processor runner for this stream.
*/
processorRunner;
/**
* The message list for this stream.
*/
messageList;
/**
* Trace ID for this execution.
*/
traceId;
/**
* Root span ID for this execution, identifying the top-level span in the trace.
*/
spanId;
messageId;
constructor({
model: _model,
stream,
messageList,
options,
messageId,
initialState
}) {
super({ component: "LLM", name: "MastraModelOutput" });
this.#options = options;
this.#transportRef = options.transportRef;
this.#returnScorerData = !!options.returnScorerData;
this.runId = options.runId;
const resultSpan = chunkLP4WZA6D_cjs.getRootExportSpan(options.tracingContext?.currentSpan);
this.traceId = resultSpan?.externalTraceId;
this.spanId = resultSpan?.id;
this.#model = _model;
this.messageId = messageId;
if (options.structuredOutput?.schema) {
this.#structuredOutputMode = options.structuredOutput.model ? "processor" : "direct";
}
if (options.outputProcessors?.length) {
this.processorRunner = new ProcessorRunner({
inputProcessors: [],
outputProcessors: options.outputProcessors,
logger: this.logger,
agentName: "MastraModelOutput",
processorStates: options.processorStates
});
}
this.messageList = messageList;
const self = this;
let processedStream = stream;
const processorRunner = this.processorRunner;
if (processorRunner && options.isLLMExecutionStep) {
const processorStates = options.processorStates || /* @__PURE__ */ new Map();
processedStream = stream.pipeThrough(
new web.TransformStream({
async transform(chunk, controller) {
if (chunk.type === "finish" && chunk.payload?.stepResult?.reason === "tool-calls") {
controller.enqueue(chunk);
return;
} else {
if (!processorStates.has(STRUCTURED_OUTPUT_PROCESSOR_NAME)) {
const processorIndex = processorRunner.outputProcessors.findIndex(
(p) => p.id === STRUCTURED_OUTPUT_PROCESSOR_NAME
);
if (processorIndex !== -1) {
const structuredOutputProcessor = processorRunner.outputProcessors[processorIndex];
const structuredOutputProcessorState = new ProcessorState({
processorName: structuredOutputProcessor?.name ?? STRUCTURED_OUTPUT_PROCESSOR_NAME,
tracingContext: options.tracingContext,
processorIndex,
createSpan: true
});
structuredOutputProcessorState.customState = { controller };
processorStates.set(STRUCTURED_OUTPUT_PROCESSOR_NAME, structuredOutputProcessorState);
}
} else {
const structuredOutputProcessorState = processorStates.get(STRUCTURED_OUTPUT_PROCESSOR_NAME);
if (structuredOutputProcessorState) {
structuredOutputProcessorState.customState.controller = controller;
}
}
const streamWriter = {
custom: async (data) => controller.enqueue(data)
};
const {
part: processed,
blocked,
reason,
tripwireOptions,
processorId
} = await processorRunner.processPart(
chunk,
processorStates,
chunkYGTB7A25_cjs.resolveObservabilityContext(options),
options.requestContext,
self.messageList,
0,
streamWriter
);
const enqueueTripwire = (r, opts, pid) => {
controller.enqueue({
type: "tripwire",
payload: {
reason: r || "Output processor blocked content",
retry: opts?.retry,
metadata: opts?.metadata,
processorId: pid
}
});
};
if (blocked) {
enqueueTripwire(reason, tripwireOptions, processorId);
return;
}
if (processed) {
controller.enqueue(processed);
}
const reprocessed = await processorRunner.drainReprocessParts(
processorStates,
chunkYGTB7A25_cjs.resolveObservabilityContext(options),
options.requestContext,
self.messageList,
0,
streamWriter
);
for (const r of reprocessed) {
if (r.blocked) {
enqueueTripwire(r.reason, r.tripwireOptions, r.processorId);
return;
}
if (r.part != null) {
controller.enqueue(r.part);
}
}
}
}
})
);
}
if (self.#structuredOutputMode === "direct" && self.#options.isLLMExecutionStep) {
processedStream = processedStream.pipeThrough(
createObjectStreamTransformer({
structuredOutput: self.#options.structuredOutput,
logger: self.logger
})
);
}
this.#baseStream = processedStream.pipeThrough(
new web.TransformStream({
transform: async (chunk, controller) => {
switch (chunk.type) {
case "tool-call-suspended":
case "tool-call-approval":
self.#status = "suspended";
self.#delayedPromises.suspendPayload.resolve(chunk.payload);
self.#delayedPromises.resumeSchema.resolve(chunk.payload.resumeSchema);
if (!self.#finishCallbackSent) {
self.#finishCallbackSent = true;
await options?.onFinish?.(self.#createSuspendedOnFinishPayload(chunk));
}
break;
case "abort":
self.#status = "canceled";
if (!self.#finishCallbackSent) {
self.#finishCallbackSent = true;
await options?.onFinish?.(self.#createAbortedOnFinishPayload());
}
self.#closeTransportIfNeeded();
break;
case "raw":
if (!self.#options.includeRawChunks) {
return;
}
break;
case "object-result":
self.#bufferedObject = chunk.object;
if (self.#delayedPromises.object.status.type === "pending") {
self.#delayedPromises.object.resolve(chunk.object);
}
break;
case "source":
self.#bufferedSources.push(chunk);
self.#bufferedByStep.sources.push(chunk);
break;
case "text-delta":
self.#bufferedText.push(chunk.payload.text);
self.#bufferedByStep.text += chunk.payload.text;
if (chunk.payload.id) {
const ary = self.#bufferedTextChunks[chunk.payload.id] ?? [];
ary.push(chunk.payload.text);
self.#bufferedTextChunks[chunk.payload.id] = ary;
}
break;
case "tool-call-input-streaming-start":
self.#toolCallDeltaIdNameMap[chunk.payload.toolCallId] = chunk.payload.toolName;
self.#toolCallStreamingMeta[chunk.payload.toolCallId] = {
toolName: chunk.payload.toolName,
providerExecuted: chunk.payload.providerExecuted,
providerMetadata: chunk.payload.providerMetadata,
dynamic: chunk.payload.dynamic,
...chunk.payload.observability ? { observability: chunk.payload.observability } : {}
};
break;
case "tool-call-input-streaming-end": {
const toolCallId = chunk.payload.toolCallId;
const meta = self.#toolCallStreamingMeta[toolCallId];
const deltaParts = self.#toolCallArgsDeltas[toolCallId];
let args = {};
if (deltaParts?.length) {
try {
const merged = deltaParts.join("");
args = typeof merged === "string" && merged.length > 0 ? JSON.parse(merged) : {};
} catch {
args = {};
}
}
delete self.#toolCallStreamingMeta[toolCallId];
delete self.#toolCallArgsDeltas[toolCallId];
delete self.#toolCallDeltaIdNameMap[toolCallId];
if (meta) {
const synthetic = {
type: "tool-call",
runId: chunk.runId,
from: chunk.from,
payload: {
toolCallId,
toolName: meta.toolName,
args,
providerExecuted: meta.providerExecuted,
providerMetadata: meta.providerMetadata,
dynamic: meta.dynamic,
...meta.observability ? { observability: meta.observability } : {}
}
};
self.#toolCalls.push(synthetic);
self.#bufferedByStep.toolCalls.push(synthetic);
self.#emitChunk(chunk);
controller.enqueue(chunk);
self.#emitChunk(synthetic);
controller.enqueue(synthetic);
return;
}
break;
}
case "tool-call-delta":
if (!self.#toolCallArgsDeltas[chunk.payload.toolCallId]) {
self.#toolCallArgsDeltas[chunk.payload.toolCallId] = [];
}
self.#toolCallArgsDeltas?.[chunk.payload.toolCallId]?.push(chunk.payload.argsTextDelta);
chunk.payload.toolName ||= self.#toolCallDeltaIdNameMap[chunk.payload.toolCallId];
break;
case "file":
self.#bufferedFiles.push(chunk);
self.#bufferedByStep.files.push(chunk);
break;
case "reasoning-start":
self.#bufferedReasoningDetails[chunk.payload.id] = {
type: "reasoning",
runId: chunk.runId,
from: chunk.from,
payload: {
id: chunk.payload.id,
providerMetadata: chunk.payload.providerMetadata,
text: ""
}
};
break;
case "reasoning-delta": {
self.#bufferedReasoning.push({
type: "reasoning",
runId: chunk.runId,
from: chunk.from,
payload: chunk.payload
});
self.#bufferedByStep.reasoning.push({
type: "reasoning",
runId: chunk.runId,
from: chunk.from,
payload: chunk.payload
});
const bufferedReasoning = self.#bufferedReasoningDetails[chunk.payload.id];
if (bufferedReasoning) {
bufferedReasoning.payload.text += chunk.payload.text;
if (chunk.payload.providerMetadata) {
bufferedReasoning.payload.providerMetadata = chunk.payload.providerMetadata;
}
}
break;
}
case "reasoning-end": {
const bufferedReasoning = self.#bufferedReasoningDetails[chunk.payload.id];
if (chunk.payload.providerMetadata && bufferedReasoning) {
bufferedReasoning.payload.providerMetadata = chunk.payload.providerMetadata;
}
break;
}
case "tool-call": {
const existingSynthetic = self.#toolCalls.find((tc) => tc.payload.toolCallId === chunk.payload.toolCallId);
if (existingSynthetic) {
if (chunk.payload.args && Object.keys(existingSynthetic.payload.args || {}).length === 0) {
existingSynthetic.payload.args = chunk.payload.args;
}
if (chunk.payload.providerMetadata && !existingSynthetic.payload.providerMetadata) {
existingSynthetic.payload.providerMetadata = chunk.payload.providerMetadata;
}
if (chunk.payload.dynamic != null && existingSynthetic.payload.dynamic == null) {
existingSynthetic.payload.dynamic = chunk.payload.dynamic;
}
if (chunk.payload.observability && !existingSynthetic.payload.observability) {
existingSynthetic.payload.observability = chunk.payload.observability;
}
return;
}
self.#toolCalls.push(chunk);
self.#bufferedByStep.toolCalls.push(chunk);
const toolCallPayload = chunk.payload;
if (toolCallPayload?.output?.from === "AGENT" && toolCallPayload?.output?.type === "finish") {
const finishPayload = toolCallPayload.output.payload;
if (finishPayload?.usage) {
self.updateUsageCount(finishPayload.usage);
}
}
break;
}
case "tool-result":
self.#toolResults.push(chunk);
self.#bufferedByStep.toolResults.push(chunk);
break;
case "step-finish": {
self.updateUsageCount(chunk.payload.output.usage);
self.#warnings = chunk.payload.stepResult.warnings || [];
if (chunk.payload.metadata.request) {
self.#request = chunk.payload.metadata.request;
}
const { providerMetadata, request, ...otherMetadata } = chunk.payload.metadata;
const payloadSteps = chunk.payload.output?.steps || [];
const currentPayloadStep = payloadSteps[payloadSteps.length - 1];
const stepTripwire = currentPayloadStep?.tripwire;
const stepText = stepTripwire ? "" : self.#bufferedByStep.text;
const stepResult = {
stepType: self.#bufferedSteps.length === 0 ? "initial" : "tool-result",
sources: self.#bufferedByStep.sources,
files: self.#bufferedByStep.files,
toolCalls: self.#bufferedByStep.toolCalls,
toolResults: self.#bufferedByStep.toolResults,
// Durable agents attach pre-computed step content on the
// step-finish chunk because the stream adapter's messageList
// may be a stale reference (each workflow step deserializes
// a fresh instance). Fall back to the live messageList for
// non-durable agents.
content: chunk.payload?._durableStepContent ?? messageList.get.response.aiV5.modelContent(-1),
text: stepText,
// Include tripwire data if present
tripwire: stepTripwire,
reasoningText: self.#bufferedReasoning.map((reasoningPart) => reasoningPart.payload.text).join(""),
reasoning: Object.values(self.#bufferedReasoningDetails),
get staticToolCalls() {
return self.#bufferedByStep.toolCalls.filter(
(part) => part.type === "tool-call" && part.payload?.dynamic === false
);
},
get dynamicToolCalls() {
return self.#bufferedByStep.toolCalls.filter(
(part) => part.type === "tool-call" && part.payload?.dynamic === true
);
},
get staticToolResults() {
return self.#bufferedByStep.toolResults.filter(
(part) => part.type === "tool-result" && part.payload?.dynamic === false
);
},
get dynamicToolResults() {
return self.#bufferedByStep.toolResults.filter(
(part) => part.type === "tool-result" && part.payload?.dynamic === true
);
},
finishReason: chunk.payload.stepResult.reason,
usage: chunk.payload.output.usage,
warnings: self.#warnings,
request: request || {},
response: {
id: chunk.payload.id || "",
timestamp: chunk.payload.metadata?.timestamp || /* @__PURE__ */ new Date(),
...otherMetadata,
modelId: chunk.payload.metadata?.modelId || chunk.payload.metadata?.model || "",
messages: chunk.payload.messages?.nonUser || [],
dbMessages: self.messageList.get.response.db(),
// We have to cast this until messageList can take generics also and type metadata, it was too
// complicated to do this in this PR, it will require a much bigger change.
uiMessages: messageList.get.response.aiV5.ui()
},
providerMetadata: providerMetadata ?? chunk.payload.providerMetadata
};
await options?.onStepFinish?.({
...self.#model.modelId && self.#model.provider && self.#model.version ? { model: self.#model } : {},
...stepResult
});
self.#bufferedSteps.push(stepResult);
self.#bufferedByStep = {
text: "",
reasoning: [],
sources: [],
files: [],
toolCalls: [],
toolResults: [],
dynamicToolCalls: [],
dynamicToolResults: [],
staticToolCalls: [],
staticToolResults: [],
content: [],
usage: { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0 },
warnings: [],
request: {},
response: {
id: "",
timestamp: /* @__PURE__ */ new Date(),
modelId: "",
messages: [],
uiMessages: []
},
reasoningText: "",
providerMetadata: void 0,
finishReason: void 0
};
break;
}
case "tripwire":
self.#tripwire = {
reason: chunk.payload?.reason || "Content blocked",
retry: chunk.payload?.retry,
metadata: chunk.payload?.metadata,
processorId: chunk.payload?.processorId
};
self.#finishReason = "other";
self.#streamFinished = true;
self.resolvePromises({
text: self.#bufferedText.join(""),
finishReason: "other",
object: void 0,
usage: self.#usageCount,
warnings: self.#warnings,
providerMetadata: void 0,
response: {
dbMessages: self.messageList.get.response.db()
},
request: {},
reasoning: [],
reasoningText: void 0,
sources: [],
files: [],
toolCalls: [],
toolResults: [],
steps: self.#bufferedSteps,
totalUsage: self.#usageCount,
content: [],
suspendPayload: void 0,
// Tripwire doesn't suspend, so resolve to undefined
resumeSchema: void 0
});
self.#closeTransportIfNeeded();
self.#emitChunk(chunk);
controller.enqueue(chunk);
self.#emitter.emit("finish");
controller.terminate();
return;
case "finish":
self.#status = "success";
if (chunk.payload.stepResult.reason) {
self.#finishReason = chunk.payload.stepResult.reason;
}
const finalProviderMetadata = chunk.payload.metadata?.providerMetadata ?? chunk.payload.providerMetadata;
if (finalProviderMetadata) {
self.#stepProviderMetadata = finalProviderMetadata;
}
if (chunk.payload.stepResult.reason === "tripwire") {
const outputSteps = chunk.payload.output?.steps;
const lastStep = outputSteps?.[outputSteps?.length - 1];
const stepTripwire = lastStep?.tripwire;
self.#tripwire = {
reason: stepTripwire?.reason || "Processor tripwire triggered",
retry: stepTripwire?.retry,
metadata: stepTripwire?.metadata,
processorId: stepTripwire?.processorId
};
}
if (self.#bufferedObject !== void 0) {
const responseMessages = messageList.get.response.db();
const lastAssistantMessage = [...responseMessages].reverse().find((m) => m.role === "assistant");
if (lastAssistantMessage) {
if (!lastAssistantMessage.content.metadata) {
lastAssistantMessage.content.metadata = {};
}
lastAssistantMessage.content.metadata.structuredOutput = self.#bufferedObject;
}
}
let response = {};
if (chunk.payload.metadata) {
const { providerMetadata, request, ...otherMetadata } = chunk.payload.metadata;
response = {
...otherMetadata,
messages: messageList.get.response.aiV5.model(),
uiMessages: messageList.get.response.aiV5.ui()
};
}
this.populateUsageCount(chunk.payload.output.usage);
chunk.payload.output.usage = {
inputTokens: self.#usageCount.inputTokens ?? 0,
outputTokens: self.#usageCount.outputTokens ?? 0,
totalTokens: self.#usageCount.totalTokens ?? 0,
...self.#usageCount.reasoningTokens !== void 0 && {
reasoningTokens: self.#usageCount.reasoningTokens
},
...self.#usageCount.cachedInputTokens !== void 0 && {
cachedInputTokens: self.#usageCount.cachedInputTokens
},
...self.#usageCount.cacheCreationInputTokens !== void 0 && {
cacheCreationInputTokens: self.#usageCount.cacheCreationInputTokens
},
...self.#usageCount.raw !== void 0 && {
raw: self.#usageCount.raw
}
};
try {
if (self.processorRunner && !self.#options.isLLMExecutionStep) {
const lastStep = self.#bufferedSteps[self.#bufferedSteps.length - 1];
const originalText = lastStep?.text || "";
const outputResultWriter = {
custom: async (data) => {
self.#emitChunk(data);
controller.enqueue(data);
}
};
const outputResult = {
text: self.#bufferedText.join(""),
usage: chunk.payload.output.usage,
finishReason: self.#finishReason || "unknown",
steps: [...self.#bufferedSteps]
};
self.messageList = await self.processorRunner.runOutputProcessors(
self.messageList,
chunkYGTB7A25_cjs.resolveObservabilityContext(options),
self.#options.requestContext,
0,
outputResultWriter,
outputResult
);
const responseMessages = self.messageList.get.response.aiV4.core();
const lastResponseMessage = responseMessages[responseMessages.length - 1];
const outputText = lastResponseMessage ? chunkRWGBZBPV_cjs.coreContentToString(lastResponseMessage.content) : "";
if (lastStep && outputText && outputText !== originalText) {
lastStep.text = outputText;
}
this.resolvePromises({
text: outputText || originalText,
finishReason: self.#finishReason
});
if (chunk.payload.metadata) {
const { providerMetadata, re