@mastra/ai-sdk 1.7.2 → 1.8.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/chat-route.d.ts +6 -2
- package/dist/chat-route.d.ts.map +1 -1
- package/dist/index.cjs +262 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +262 -62
- package/dist/index.js.map +1 -1
- package/dist/sse-heartbeat.d.ts +8 -0
- package/dist/sse-heartbeat.d.ts.map +1 -0
- package/dist/transformers.d.ts +13 -5
- package/dist/transformers.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type { WorkflowDataPart, WorkflowStepDataPart } from './transformers.js';
|
|
|
6
6
|
export { networkRoute, handleNetworkStream } from './network-route.js';
|
|
7
7
|
export type { NetworkRouteOptions, NetworkStreamHandlerParams, NetworkStreamHandlerOptions } from './network-route.js';
|
|
8
8
|
export type { NetworkDataPart } from './transformers.js';
|
|
9
|
-
export type { AgentDataPart } from './transformers.js';
|
|
9
|
+
export type { AgentDataPart, AgentStepDataPart } from './transformers.js';
|
|
10
10
|
export { toAISdkStream, toAISdkV5Stream } from './convert-streams.js';
|
|
11
11
|
export { workflowSnapshotToStream } from './convert-snapshot.js';
|
|
12
12
|
export { smoothStream } from './smooth-stream.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AACxH,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AACpH,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AACxH,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AACpH,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAG3F,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAGhH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9404,6 +9404,7 @@ function convertFullStreamChunkToUIMessageStream({ part, messageMetadataValue, s
|
|
|
9404
9404
|
//#endregion
|
|
9405
9405
|
//#region src/transformers.ts
|
|
9406
9406
|
const PRIMITIVE_CACHE_SYMBOL = Symbol("primitive-cache");
|
|
9407
|
+
const COMPLETED_STEPS_SYMBOL = Symbol("completed-steps-cache");
|
|
9407
9408
|
function cloneWorkflowStep(step, includeOutput) {
|
|
9408
9409
|
return {
|
|
9409
9410
|
name: step.name,
|
|
@@ -9531,7 +9532,8 @@ function createAgentStreamToAISDKTransformer(convertMastraChunkToAISDK, { lastMe
|
|
|
9531
9532
|
if (transformedChunk) if (transformedChunk.type === "tool-agent") {
|
|
9532
9533
|
const payload = transformedChunk.payload;
|
|
9533
9534
|
const agentTransformed = transformAgent(payload, bufferedSteps);
|
|
9534
|
-
if (agentTransformed) controller.enqueue(
|
|
9535
|
+
if (agentTransformed) if (Array.isArray(agentTransformed)) for (const part of agentTransformed) controller.enqueue(part);
|
|
9536
|
+
else controller.enqueue(agentTransformed);
|
|
9535
9537
|
} else if (transformedChunk.type === "tool-workflow") {
|
|
9536
9538
|
const payload = transformedChunk.payload;
|
|
9537
9539
|
const workflowChunk = transformWorkflow(payload, bufferedSteps, true, void 0, void 0, convertMastraChunkToAISDK);
|
|
@@ -9614,30 +9616,7 @@ function AgentStreamToAISDKV6Transformer({ lastMessageId, sendStart = true, send
|
|
|
9614
9616
|
});
|
|
9615
9617
|
}
|
|
9616
9618
|
function ensureAgentRunState(bufferedSteps, runId) {
|
|
9617
|
-
if (!bufferedSteps.has(runId)) bufferedSteps.set(runId,
|
|
9618
|
-
id: "",
|
|
9619
|
-
object: null,
|
|
9620
|
-
finishReason: null,
|
|
9621
|
-
usage: null,
|
|
9622
|
-
warnings: [],
|
|
9623
|
-
text: "",
|
|
9624
|
-
reasoning: [],
|
|
9625
|
-
sources: [],
|
|
9626
|
-
files: [],
|
|
9627
|
-
toolCalls: [],
|
|
9628
|
-
pendingToolCalls: [],
|
|
9629
|
-
toolResults: [],
|
|
9630
|
-
request: {},
|
|
9631
|
-
response: {
|
|
9632
|
-
id: "",
|
|
9633
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
9634
|
-
modelId: "",
|
|
9635
|
-
messages: []
|
|
9636
|
-
},
|
|
9637
|
-
providerMetadata: void 0,
|
|
9638
|
-
steps: [],
|
|
9639
|
-
status: "running"
|
|
9640
|
-
});
|
|
9619
|
+
if (!bufferedSteps.has(runId)) bufferedSteps.set(runId, createAgentRunState());
|
|
9641
9620
|
return bufferedSteps.get(runId);
|
|
9642
9621
|
}
|
|
9643
9622
|
function upsertPendingToolCall(pendingToolCalls = [], toolCallId, updates) {
|
|
@@ -9673,36 +9652,109 @@ function appendPendingToolCallArgs(pendingToolCalls = [], payload) {
|
|
|
9673
9652
|
function removePendingToolCall(pendingToolCalls = [], toolCallId) {
|
|
9674
9653
|
return pendingToolCalls.filter((call) => call.toolCallId !== toolCallId);
|
|
9675
9654
|
}
|
|
9655
|
+
function createAgentResponseState() {
|
|
9656
|
+
return {
|
|
9657
|
+
id: "",
|
|
9658
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
9659
|
+
modelId: "",
|
|
9660
|
+
messages: []
|
|
9661
|
+
};
|
|
9662
|
+
}
|
|
9663
|
+
function createAgentRunState(id = "") {
|
|
9664
|
+
return {
|
|
9665
|
+
id,
|
|
9666
|
+
object: null,
|
|
9667
|
+
finishReason: null,
|
|
9668
|
+
usage: null,
|
|
9669
|
+
warnings: [],
|
|
9670
|
+
text: "",
|
|
9671
|
+
reasoning: [],
|
|
9672
|
+
sources: [],
|
|
9673
|
+
files: [],
|
|
9674
|
+
toolCalls: [],
|
|
9675
|
+
pendingToolCalls: [],
|
|
9676
|
+
toolResults: [],
|
|
9677
|
+
request: {},
|
|
9678
|
+
response: createAgentResponseState(),
|
|
9679
|
+
providerMetadata: void 0,
|
|
9680
|
+
steps: [],
|
|
9681
|
+
status: "running"
|
|
9682
|
+
};
|
|
9683
|
+
}
|
|
9684
|
+
function cloneAgentResponse(response, { includeMessages }) {
|
|
9685
|
+
if (!response) return response;
|
|
9686
|
+
return {
|
|
9687
|
+
...response,
|
|
9688
|
+
...Object.prototype.hasOwnProperty.call(response, "messages") ? { messages: includeMessages ? response.messages : [] } : {},
|
|
9689
|
+
...Object.prototype.hasOwnProperty.call(response, "dbMessages") ? { dbMessages: includeMessages ? response.dbMessages : [] } : {},
|
|
9690
|
+
...Object.prototype.hasOwnProperty.call(response, "uiMessages") ? { uiMessages: includeMessages ? response.uiMessages : [] } : {}
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
9693
|
+
function cloneAgentStep(step, { includeDetails }) {
|
|
9694
|
+
if (includeDetails) return {
|
|
9695
|
+
...step,
|
|
9696
|
+
response: cloneAgentResponse(step.response, { includeMessages: true })
|
|
9697
|
+
};
|
|
9698
|
+
return {
|
|
9699
|
+
...step,
|
|
9700
|
+
object: null,
|
|
9701
|
+
files: [],
|
|
9702
|
+
sources: [],
|
|
9703
|
+
toolCalls: [],
|
|
9704
|
+
pendingToolCalls: [],
|
|
9705
|
+
toolResults: [],
|
|
9706
|
+
dynamicToolCalls: [],
|
|
9707
|
+
dynamicToolResults: [],
|
|
9708
|
+
staticToolCalls: [],
|
|
9709
|
+
staticToolResults: [],
|
|
9710
|
+
text: "",
|
|
9711
|
+
reasoning: [],
|
|
9712
|
+
content: Array.isArray(step.content) ? [] : step.content,
|
|
9713
|
+
reasoningText: typeof step.reasoningText === "string" ? "" : step.reasoningText,
|
|
9714
|
+
response: cloneAgentResponse(step.response, { includeMessages: false })
|
|
9715
|
+
};
|
|
9716
|
+
}
|
|
9717
|
+
function serializeAgentRun(current, { includeCompletedStepDetails, includeResponseMessages }) {
|
|
9718
|
+
const { _textOffset: _to, _reasoningOffset: _ro, ...data } = current;
|
|
9719
|
+
return {
|
|
9720
|
+
...data,
|
|
9721
|
+
response: cloneAgentResponse(data.response, { includeMessages: includeResponseMessages }),
|
|
9722
|
+
steps: data.steps.map((step) => cloneAgentStep(step, { includeDetails: includeCompletedStepDetails }))
|
|
9723
|
+
};
|
|
9724
|
+
}
|
|
9725
|
+
function createAgentDataPart(args) {
|
|
9726
|
+
const { current, runId, includeCompletedStepDetails, includeResponseMessages } = args;
|
|
9727
|
+
return {
|
|
9728
|
+
type: "data-tool-agent",
|
|
9729
|
+
id: runId,
|
|
9730
|
+
data: serializeAgentRun(current, {
|
|
9731
|
+
includeCompletedStepDetails,
|
|
9732
|
+
includeResponseMessages
|
|
9733
|
+
})
|
|
9734
|
+
};
|
|
9735
|
+
}
|
|
9736
|
+
function createAgentStepDataPart(args) {
|
|
9737
|
+
const { runId, stepIndex, step } = args;
|
|
9738
|
+
return {
|
|
9739
|
+
type: "data-tool-agent-step",
|
|
9740
|
+
id: `${runId}:${stepIndex}`,
|
|
9741
|
+
data: {
|
|
9742
|
+
runId,
|
|
9743
|
+
stepIndex,
|
|
9744
|
+
step: cloneAgentStep(step, { includeDetails: true })
|
|
9745
|
+
}
|
|
9746
|
+
};
|
|
9747
|
+
}
|
|
9676
9748
|
function transformAgent(payload, bufferedSteps) {
|
|
9677
9749
|
let hasChanged = false;
|
|
9750
|
+
let completedStep = null;
|
|
9678
9751
|
switch (payload.type) {
|
|
9679
|
-
case "start":
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
object: null,
|
|
9683
|
-
finishReason: null,
|
|
9684
|
-
usage: null,
|
|
9685
|
-
warnings: [],
|
|
9686
|
-
text: "",
|
|
9687
|
-
reasoning: [],
|
|
9688
|
-
sources: [],
|
|
9689
|
-
files: [],
|
|
9690
|
-
toolCalls: [],
|
|
9691
|
-
pendingToolCalls: [],
|
|
9692
|
-
toolResults: [],
|
|
9693
|
-
request: {},
|
|
9694
|
-
response: {
|
|
9695
|
-
id: "",
|
|
9696
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
9697
|
-
modelId: "",
|
|
9698
|
-
messages: []
|
|
9699
|
-
},
|
|
9700
|
-
providerMetadata: void 0,
|
|
9701
|
-
steps: [],
|
|
9702
|
-
status: "running"
|
|
9703
|
-
});
|
|
9752
|
+
case "start": {
|
|
9753
|
+
const startState = createAgentRunState(payload.payload.id);
|
|
9754
|
+
bufferedSteps.set(payload.runId, startState);
|
|
9704
9755
|
hasChanged = true;
|
|
9705
9756
|
break;
|
|
9757
|
+
}
|
|
9706
9758
|
case "tool-call-input-streaming-start": {
|
|
9707
9759
|
const toolInputStartRun = ensureAgentRunState(bufferedSteps, payload.runId);
|
|
9708
9760
|
const existing = toolInputStartRun.pendingToolCalls?.find((call) => call.toolCallId === payload.payload.toolCallId);
|
|
@@ -9833,6 +9885,7 @@ function transformAgent(payload, bufferedSteps) {
|
|
|
9833
9885
|
break;
|
|
9834
9886
|
case "step-finish": {
|
|
9835
9887
|
const stepRun = ensureAgentRunState(bufferedSteps, payload.runId);
|
|
9888
|
+
const stepIndex = stepRun.steps.length;
|
|
9836
9889
|
const { steps: _steps, _textOffset, _reasoningOffset, ...stepRunWithoutSteps } = stepRun;
|
|
9837
9890
|
const textOffset = _textOffset || 0;
|
|
9838
9891
|
const reasoningOffset = _reasoningOffset || 0;
|
|
@@ -9874,18 +9927,28 @@ function transformAgent(payload, bufferedSteps) {
|
|
|
9874
9927
|
_textOffset: stepRun.text.length,
|
|
9875
9928
|
_reasoningOffset: stepRun.reasoning.length
|
|
9876
9929
|
});
|
|
9930
|
+
completedStep = {
|
|
9931
|
+
stepIndex,
|
|
9932
|
+
step: stepResult
|
|
9933
|
+
};
|
|
9877
9934
|
hasChanged = true;
|
|
9878
9935
|
break;
|
|
9879
9936
|
}
|
|
9880
9937
|
default: break;
|
|
9881
9938
|
}
|
|
9882
9939
|
if (hasChanged) {
|
|
9883
|
-
const
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
};
|
|
9940
|
+
const snapshot = createAgentDataPart({
|
|
9941
|
+
current: bufferedSteps.get(payload.runId),
|
|
9942
|
+
runId: payload.runId,
|
|
9943
|
+
includeCompletedStepDetails: payload.type === "finish",
|
|
9944
|
+
includeResponseMessages: payload.type === "finish"
|
|
9945
|
+
});
|
|
9946
|
+
if (completedStep) return [snapshot, createAgentStepDataPart({
|
|
9947
|
+
runId: payload.runId,
|
|
9948
|
+
stepIndex: completedStep.stepIndex,
|
|
9949
|
+
step: completedStep.step
|
|
9950
|
+
})];
|
|
9951
|
+
return snapshot;
|
|
9889
9952
|
}
|
|
9890
9953
|
return null;
|
|
9891
9954
|
}
|
|
@@ -10311,10 +10374,24 @@ function transformNetwork(payload, bufferedNetworks, isNested) {
|
|
|
10311
10374
|
const step = current.steps.find((step) => step.id === stepId);
|
|
10312
10375
|
if (!step) return null;
|
|
10313
10376
|
step[PRIMITIVE_CACHE_SYMBOL] = step[PRIMITIVE_CACHE_SYMBOL] || /* @__PURE__ */ new Map();
|
|
10377
|
+
if (payload.payload.type === "start") delete step[COMPLETED_STEPS_SYMBOL];
|
|
10314
10378
|
const result = transformAgent(payload.payload, step[PRIMITIVE_CACHE_SYMBOL]);
|
|
10315
|
-
|
|
10316
|
-
|
|
10379
|
+
const snapshot = Array.isArray(result) ? result[0] : result;
|
|
10380
|
+
if (snapshot) {
|
|
10381
|
+
const { request, response, ...data } = snapshot.data;
|
|
10382
|
+
if (Array.isArray(result)) {
|
|
10383
|
+
const { stepIndex, step: completedStepDetail } = result[1].data;
|
|
10384
|
+
step[COMPLETED_STEPS_SYMBOL] = step[COMPLETED_STEPS_SYMBOL] || /* @__PURE__ */ new Map();
|
|
10385
|
+
step[COMPLETED_STEPS_SYMBOL].set(stepIndex, completedStepDetail);
|
|
10386
|
+
}
|
|
10317
10387
|
step.task = data;
|
|
10388
|
+
const completedSteps = step[COMPLETED_STEPS_SYMBOL];
|
|
10389
|
+
if (completedSteps && completedSteps.size > 0 && Array.isArray(data.steps)) {
|
|
10390
|
+
for (const [stepIndex, completedStepDetail] of completedSteps) if (stepIndex < data.steps.length) data.steps[stepIndex] = {
|
|
10391
|
+
...data.steps[stepIndex],
|
|
10392
|
+
...completedStepDetail
|
|
10393
|
+
};
|
|
10394
|
+
}
|
|
10318
10395
|
}
|
|
10319
10396
|
bufferedNetworks.set(payload.runId, current);
|
|
10320
10397
|
return {
|
|
@@ -10438,6 +10515,124 @@ function toAISdkStream(stream, options = {
|
|
|
10438
10515
|
}));
|
|
10439
10516
|
}
|
|
10440
10517
|
//#endregion
|
|
10518
|
+
//#region src/sse-heartbeat.ts
|
|
10519
|
+
const SSE_HEARTBEAT_BYTES = new TextEncoder().encode(": heartbeat\n\n");
|
|
10520
|
+
const LF_BYTE = 10;
|
|
10521
|
+
const MAX_TIMEOUT_MS = 2147483647;
|
|
10522
|
+
/** Throws when an enabled heartbeat interval cannot be scheduled with a timer. */
|
|
10523
|
+
function assertValidHeartbeatMs(heartbeatMs) {
|
|
10524
|
+
if (heartbeatMs !== void 0 && !(heartbeatMs <= 0) && (!Number.isFinite(heartbeatMs) || heartbeatMs > MAX_TIMEOUT_MS)) throw new RangeError(`heartbeatMs must be a finite number no greater than ${MAX_TIMEOUT_MS}`);
|
|
10525
|
+
}
|
|
10526
|
+
/**
|
|
10527
|
+
* Adds periodic SSE comment heartbeats to an AI SDK response body.
|
|
10528
|
+
* AI SDK serialization uses LF-delimited frames, which this private wrapper preserves and relies on.
|
|
10529
|
+
*/
|
|
10530
|
+
function withSseHeartbeat(response, heartbeatMs) {
|
|
10531
|
+
assertValidHeartbeatMs(heartbeatMs);
|
|
10532
|
+
if (heartbeatMs === void 0 || heartbeatMs <= 0 || !response.body) return response;
|
|
10533
|
+
const reader = response.body.getReader();
|
|
10534
|
+
let heartbeatTimeout;
|
|
10535
|
+
let wakePull;
|
|
10536
|
+
let readResult;
|
|
10537
|
+
let readError;
|
|
10538
|
+
let hasReadError = false;
|
|
10539
|
+
let reading = false;
|
|
10540
|
+
let finished = false;
|
|
10541
|
+
let readerReleased = false;
|
|
10542
|
+
let atFrameBoundary = true;
|
|
10543
|
+
let lastByte;
|
|
10544
|
+
let nextHeartbeatAt = performance.now() + heartbeatMs;
|
|
10545
|
+
const clearHeartbeat = () => {
|
|
10546
|
+
if (heartbeatTimeout !== void 0) {
|
|
10547
|
+
clearTimeout(heartbeatTimeout);
|
|
10548
|
+
heartbeatTimeout = void 0;
|
|
10549
|
+
}
|
|
10550
|
+
};
|
|
10551
|
+
const releaseReader = () => {
|
|
10552
|
+
if (readerReleased) return;
|
|
10553
|
+
readerReleased = true;
|
|
10554
|
+
reader.releaseLock();
|
|
10555
|
+
};
|
|
10556
|
+
const updateFrameBoundary = (chunk) => {
|
|
10557
|
+
if (chunk.byteLength === 0) return;
|
|
10558
|
+
atFrameBoundary = chunk.byteLength === 1 ? lastByte === LF_BYTE && chunk[0] === LF_BYTE : chunk[chunk.byteLength - 2] === LF_BYTE && chunk[chunk.byteLength - 1] === LF_BYTE;
|
|
10559
|
+
lastByte = chunk[chunk.byteLength - 1];
|
|
10560
|
+
};
|
|
10561
|
+
const startRead = () => {
|
|
10562
|
+
if (reading || readResult || hasReadError || finished) return;
|
|
10563
|
+
reading = true;
|
|
10564
|
+
reader.read().then((result) => {
|
|
10565
|
+
reading = false;
|
|
10566
|
+
readResult = result;
|
|
10567
|
+
wakePull?.("read");
|
|
10568
|
+
}, (error) => {
|
|
10569
|
+
reading = false;
|
|
10570
|
+
readError = error;
|
|
10571
|
+
hasReadError = true;
|
|
10572
|
+
wakePull?.("read");
|
|
10573
|
+
});
|
|
10574
|
+
};
|
|
10575
|
+
const forwardRead = (controller) => {
|
|
10576
|
+
if (hasReadError) {
|
|
10577
|
+
finished = true;
|
|
10578
|
+
releaseReader();
|
|
10579
|
+
controller.error(readError);
|
|
10580
|
+
return;
|
|
10581
|
+
}
|
|
10582
|
+
if (!readResult) return;
|
|
10583
|
+
const result = readResult;
|
|
10584
|
+
readResult = void 0;
|
|
10585
|
+
if (result.done) {
|
|
10586
|
+
finished = true;
|
|
10587
|
+
releaseReader();
|
|
10588
|
+
controller.close();
|
|
10589
|
+
return;
|
|
10590
|
+
}
|
|
10591
|
+
updateFrameBoundary(result.value);
|
|
10592
|
+
controller.enqueue(result.value);
|
|
10593
|
+
};
|
|
10594
|
+
const stream = new ReadableStream({
|
|
10595
|
+
async pull(controller) {
|
|
10596
|
+
if (finished) return;
|
|
10597
|
+
startRead();
|
|
10598
|
+
if (readResult || hasReadError) {
|
|
10599
|
+
forwardRead(controller);
|
|
10600
|
+
return;
|
|
10601
|
+
}
|
|
10602
|
+
const next = await new Promise((resolve) => {
|
|
10603
|
+
wakePull = resolve;
|
|
10604
|
+
if (atFrameBoundary) heartbeatTimeout = setTimeout(() => resolve("heartbeat"), Math.max(0, nextHeartbeatAt - performance.now()));
|
|
10605
|
+
});
|
|
10606
|
+
wakePull = void 0;
|
|
10607
|
+
clearHeartbeat();
|
|
10608
|
+
if (finished) return;
|
|
10609
|
+
if (readResult || hasReadError) {
|
|
10610
|
+
forwardRead(controller);
|
|
10611
|
+
return;
|
|
10612
|
+
}
|
|
10613
|
+
if (next === "heartbeat") {
|
|
10614
|
+
controller.enqueue(SSE_HEARTBEAT_BYTES.slice());
|
|
10615
|
+
nextHeartbeatAt = performance.now() + heartbeatMs;
|
|
10616
|
+
}
|
|
10617
|
+
},
|
|
10618
|
+
async cancel(reason) {
|
|
10619
|
+
if (finished) return;
|
|
10620
|
+
finished = true;
|
|
10621
|
+
clearHeartbeat();
|
|
10622
|
+
try {
|
|
10623
|
+
await reader.cancel(reason);
|
|
10624
|
+
} finally {
|
|
10625
|
+
releaseReader();
|
|
10626
|
+
}
|
|
10627
|
+
}
|
|
10628
|
+
});
|
|
10629
|
+
return new Response(stream, {
|
|
10630
|
+
status: response.status,
|
|
10631
|
+
statusText: response.statusText,
|
|
10632
|
+
headers: response.headers
|
|
10633
|
+
});
|
|
10634
|
+
}
|
|
10635
|
+
//#endregion
|
|
10441
10636
|
//#region src/chat-route.ts
|
|
10442
10637
|
/**
|
|
10443
10638
|
* Collects every approval response across all assistant messages in a v6
|
|
@@ -10640,11 +10835,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
|
|
|
10640
10835
|
* @param {boolean} [options.sendFinish=true] - Whether to send finish events in the stream
|
|
10641
10836
|
* @param {boolean} [options.sendReasoning=false] - Whether to include reasoning steps in the stream
|
|
10642
10837
|
* @param {boolean} [options.sendSources=false] - Whether to include source citations in the stream
|
|
10838
|
+
* @param {number} [options.heartbeatMs] - Target interval for periodic SSE comment heartbeats. Already-buffered source events and stream lifecycle signals take priority. Values up to 0 disable heartbeats. `NaN`, positive infinity, and values above 2,147,483,647 throw a `RangeError`.
|
|
10643
10839
|
* @param {(error: unknown) => string} [options.onError] - Custom error serializer streamed to the client. When omitted, errors are passed through a default serializer that strips sensitive fields (e.g. `APICallError.requestBodyValues`, which holds the system prompt) before they reach the client.
|
|
10644
10840
|
*
|
|
10645
10841
|
* @returns {ReturnType<typeof registerApiRoute>} A registered API route handler
|
|
10646
10842
|
*
|
|
10647
10843
|
* @throws {Error} When path doesn't include `:agentId` and no fixed agent is specified
|
|
10844
|
+
* @throws {RangeError} When `heartbeatMs` is `NaN`, positive infinity, or greater than 2,147,483,647
|
|
10648
10845
|
* @throws {Error} When agent ID is missing at runtime
|
|
10649
10846
|
* @throws {Error} When specified agent is not found in Mastra instance
|
|
10650
10847
|
*
|
|
@@ -10667,12 +10864,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
|
|
|
10667
10864
|
* @remarks
|
|
10668
10865
|
* - The route handler expects a JSON body with a `messages` array
|
|
10669
10866
|
* - Messages should follow the format: `{ role: 'user' | 'assistant' | 'system', content: string }`
|
|
10670
|
-
* - The response is a Server-Sent Events (SSE) stream compatible with AI SDK
|
|
10867
|
+
* - The response is a Server-Sent Events (SSE) stream compatible with the selected AI SDK version
|
|
10671
10868
|
* - If both `agent` and `:agentId` are present, a warning is logged and the fixed `agent` takes precedence
|
|
10672
10869
|
* - Request context from the incoming request overrides `defaultOptions.requestContext` if both are present
|
|
10673
10870
|
*/
|
|
10674
|
-
function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, onError }) {
|
|
10871
|
+
function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, heartbeatMs, onError }) {
|
|
10675
10872
|
if (!agent && !path.includes("/:agentId")) throw new Error("Path must include :agentId to route to the correct agent or pass the agent explicitly");
|
|
10873
|
+
assertValidHeartbeatMs(heartbeatMs);
|
|
10676
10874
|
return registerApiRoute(path, {
|
|
10677
10875
|
method: "POST",
|
|
10678
10876
|
openapi: {
|
|
@@ -10801,11 +10999,13 @@ function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimenta
|
|
|
10801
10999
|
sendSources,
|
|
10802
11000
|
onError
|
|
10803
11001
|
};
|
|
10804
|
-
|
|
11002
|
+
let response;
|
|
11003
|
+
if (version === "v6") response = createUIMessageStreamResponse({ stream: await handleChatStream({
|
|
10805
11004
|
...handlerOptions,
|
|
10806
11005
|
version: "v6"
|
|
10807
11006
|
}) });
|
|
10808
|
-
|
|
11007
|
+
else response = createUIMessageStreamResponse$1({ stream: await handleChatStream(handlerOptions) });
|
|
11008
|
+
return withSseHeartbeat(response, heartbeatMs);
|
|
10809
11009
|
}
|
|
10810
11010
|
});
|
|
10811
11011
|
}
|