@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @mastra/ai-sdk
|
|
2
2
|
|
|
3
|
+
## 1.8.0-alpha.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added optional SSE heartbeats to `chatRoute()` so streams can remain active through infrastructure with idle timeouts. ([#20852](https://github.com/mastra-ai/mastra/pull/20852))
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
chatRoute({
|
|
11
|
+
path: '/chat/:agentId',
|
|
12
|
+
heartbeatMs: 15_000,
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Fixed nested agent streams to emit compact snapshots until each step completes, and added `data-tool-agent-step` as a new stream part type (exported as `AgentStepDataPart`) that carries the full completed-step detail for consumers that observe suspension without a finish event. ([#20576](https://github.com/mastra-ai/mastra/pull/20576))
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`e7109ee`](https://github.com/mastra-ai/mastra/commit/e7109ee6f731bacc79c885906f3c7dca8d8f013a), [`772c0c8`](https://github.com/mastra-ai/mastra/commit/772c0c897cec383258de2e6178147f8014767c7b), [`578bf2e`](https://github.com/mastra-ai/mastra/commit/578bf2e6a88e9d5b8bf502204e15a95dfbb679ae), [`06b2d87`](https://github.com/mastra-ai/mastra/commit/06b2d87e63bcdd0ed59215c6789692b9b12de376), [`ac01d63`](https://github.com/mastra-ai/mastra/commit/ac01d6355974aec73fdb8781449ed12bac582094), [`a810a05`](https://github.com/mastra-ai/mastra/commit/a810a058f62ad407cfc1701e0be36ae91145d7cf), [`f8da216`](https://github.com/mastra-ai/mastra/commit/f8da21633e7eb0e31c9ce0fc30567870d19416d3), [`6104347`](https://github.com/mastra-ai/mastra/commit/61043473ba6bfd0a25156824e853e13165562e6c), [`45bfb88`](https://github.com/mastra-ai/mastra/commit/45bfb88fd52f1dd3be20e2a38905777c96499c90), [`e3b9307`](https://github.com/mastra-ai/mastra/commit/e3b9307098daefbfae2a52ae2ef51bc9fc701190), [`d6834c5`](https://github.com/mastra-ai/mastra/commit/d6834c5a7866b16734d23900163c2414ed70d791), [`c52d346`](https://github.com/mastra-ai/mastra/commit/c52d3462ec831a5d95926ecd3d3373f5928ad2e5), [`0023e79`](https://github.com/mastra-ai/mastra/commit/0023e7919431078280abd11c89d1edeae35fcc69), [`c2ad51e`](https://github.com/mastra-ai/mastra/commit/c2ad51e2467f901eecba8c9f4a45e22a50bd7c18), [`3dc97ea`](https://github.com/mastra-ai/mastra/commit/3dc97ea415fad353b48a13095fad1835933cc12a), [`3d01cd3`](https://github.com/mastra-ai/mastra/commit/3d01cd387321b6f9c5cac31d487c84bf51b19c78), [`7bf3086`](https://github.com/mastra-ai/mastra/commit/7bf308663f0115ca74ad20554ade740f06640859), [`a8dd139`](https://github.com/mastra-ai/mastra/commit/a8dd1391a9fe9a6632c25809ef236980afa9a020), [`e5786be`](https://github.com/mastra-ai/mastra/commit/e5786be02bb903073082bd9d6da880ebaacc343f), [`2093fbd`](https://github.com/mastra-ai/mastra/commit/2093fbd53bb744bae19ec89f6d73db9a66fbe8a7), [`e7a5da4`](https://github.com/mastra-ai/mastra/commit/e7a5da4ef8e4dd452d2f232961b4e682a85ffe43), [`7b4393d`](https://github.com/mastra-ai/mastra/commit/7b4393d557411fdcf07b0e30e5acaf7cc85154ae)]:
|
|
21
|
+
- @mastra/core@1.58.0-alpha.1
|
|
22
|
+
|
|
3
23
|
## 1.7.2
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/chat-route.d.ts
CHANGED
|
@@ -96,6 +96,8 @@ export type chatRouteOptions<OUTPUT = undefined> = {
|
|
|
96
96
|
sendFinish?: boolean;
|
|
97
97
|
sendReasoning?: boolean;
|
|
98
98
|
sendSources?: boolean;
|
|
99
|
+
/** Target interval for periodic SSE comment heartbeats. Values up to 0 disable heartbeats. `NaN`, positive infinity, and values above 2,147,483,647 throw a `RangeError`. */
|
|
100
|
+
heartbeatMs?: number;
|
|
99
101
|
onError?: (error: unknown) => string;
|
|
100
102
|
};
|
|
101
103
|
/**
|
|
@@ -111,11 +113,13 @@ export type chatRouteOptions<OUTPUT = undefined> = {
|
|
|
111
113
|
* @param {boolean} [options.sendFinish=true] - Whether to send finish events in the stream
|
|
112
114
|
* @param {boolean} [options.sendReasoning=false] - Whether to include reasoning steps in the stream
|
|
113
115
|
* @param {boolean} [options.sendSources=false] - Whether to include source citations in the stream
|
|
116
|
+
* @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`.
|
|
114
117
|
* @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.
|
|
115
118
|
*
|
|
116
119
|
* @returns {ReturnType<typeof registerApiRoute>} A registered API route handler
|
|
117
120
|
*
|
|
118
121
|
* @throws {Error} When path doesn't include `:agentId` and no fixed agent is specified
|
|
122
|
+
* @throws {RangeError} When `heartbeatMs` is `NaN`, positive infinity, or greater than 2,147,483,647
|
|
119
123
|
* @throws {Error} When agent ID is missing at runtime
|
|
120
124
|
* @throws {Error} When specified agent is not found in Mastra instance
|
|
121
125
|
*
|
|
@@ -138,10 +142,10 @@ export type chatRouteOptions<OUTPUT = undefined> = {
|
|
|
138
142
|
* @remarks
|
|
139
143
|
* - The route handler expects a JSON body with a `messages` array
|
|
140
144
|
* - Messages should follow the format: `{ role: 'user' | 'assistant' | 'system', content: string }`
|
|
141
|
-
* - The response is a Server-Sent Events (SSE) stream compatible with AI SDK
|
|
145
|
+
* - The response is a Server-Sent Events (SSE) stream compatible with the selected AI SDK version
|
|
142
146
|
* - If both `agent` and `:agentId` are present, a warning is logged and the fixed `agent` takes precedence
|
|
143
147
|
* - Request context from the incoming request overrides `defaultOptions.requestContext` if both are present
|
|
144
148
|
*/
|
|
145
|
-
export declare function chatRoute<OUTPUT = undefined>({ path, agent, defaultOptions, experimentalTransform, version, agentVersion, sendStart, sendFinish, sendReasoning, sendSources, onError, }: chatRouteOptions<OUTPUT>): ReturnType<typeof registerApiRoute>;
|
|
149
|
+
export declare function chatRoute<OUTPUT = undefined>({ path, agent, defaultOptions, experimentalTransform, version, agentVersion, sendStart, sendFinish, sendReasoning, sendSources, heartbeatMs, onError, }: chatRouteOptions<OUTPUT>): ReturnType<typeof registerApiRoute>;
|
|
146
150
|
export {};
|
|
147
151
|
//# sourceMappingURL=chat-route.d.ts.map
|
package/dist/chat-route.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-route.d.ts","sourceRoot":"","sources":["../src/chat-route.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,IAAI,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAM9F,OAAO,KAAK,EAAE,sBAAsB,IAAI,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,KAAK,EAAE,qBAAqB,EAA6B,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-route.d.ts","sourceRoot":"","sources":["../src/chat-route.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -9427,6 +9427,7 @@ function convertFullStreamChunkToUIMessageStream({ part, messageMetadataValue, s
|
|
|
9427
9427
|
//#endregion
|
|
9428
9428
|
//#region src/transformers.ts
|
|
9429
9429
|
const PRIMITIVE_CACHE_SYMBOL = Symbol("primitive-cache");
|
|
9430
|
+
const COMPLETED_STEPS_SYMBOL = Symbol("completed-steps-cache");
|
|
9430
9431
|
function cloneWorkflowStep(step, includeOutput) {
|
|
9431
9432
|
return {
|
|
9432
9433
|
name: step.name,
|
|
@@ -9554,7 +9555,8 @@ function createAgentStreamToAISDKTransformer(convertMastraChunkToAISDK, { lastMe
|
|
|
9554
9555
|
if (transformedChunk) if (transformedChunk.type === "tool-agent") {
|
|
9555
9556
|
const payload = transformedChunk.payload;
|
|
9556
9557
|
const agentTransformed = transformAgent(payload, bufferedSteps);
|
|
9557
|
-
if (agentTransformed) controller.enqueue(
|
|
9558
|
+
if (agentTransformed) if (Array.isArray(agentTransformed)) for (const part of agentTransformed) controller.enqueue(part);
|
|
9559
|
+
else controller.enqueue(agentTransformed);
|
|
9558
9560
|
} else if (transformedChunk.type === "tool-workflow") {
|
|
9559
9561
|
const payload = transformedChunk.payload;
|
|
9560
9562
|
const workflowChunk = transformWorkflow(payload, bufferedSteps, true, void 0, void 0, convertMastraChunkToAISDK);
|
|
@@ -9637,30 +9639,7 @@ function AgentStreamToAISDKV6Transformer({ lastMessageId, sendStart = true, send
|
|
|
9637
9639
|
});
|
|
9638
9640
|
}
|
|
9639
9641
|
function ensureAgentRunState(bufferedSteps, runId) {
|
|
9640
|
-
if (!bufferedSteps.has(runId)) bufferedSteps.set(runId,
|
|
9641
|
-
id: "",
|
|
9642
|
-
object: null,
|
|
9643
|
-
finishReason: null,
|
|
9644
|
-
usage: null,
|
|
9645
|
-
warnings: [],
|
|
9646
|
-
text: "",
|
|
9647
|
-
reasoning: [],
|
|
9648
|
-
sources: [],
|
|
9649
|
-
files: [],
|
|
9650
|
-
toolCalls: [],
|
|
9651
|
-
pendingToolCalls: [],
|
|
9652
|
-
toolResults: [],
|
|
9653
|
-
request: {},
|
|
9654
|
-
response: {
|
|
9655
|
-
id: "",
|
|
9656
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
9657
|
-
modelId: "",
|
|
9658
|
-
messages: []
|
|
9659
|
-
},
|
|
9660
|
-
providerMetadata: void 0,
|
|
9661
|
-
steps: [],
|
|
9662
|
-
status: "running"
|
|
9663
|
-
});
|
|
9642
|
+
if (!bufferedSteps.has(runId)) bufferedSteps.set(runId, createAgentRunState());
|
|
9664
9643
|
return bufferedSteps.get(runId);
|
|
9665
9644
|
}
|
|
9666
9645
|
function upsertPendingToolCall(pendingToolCalls = [], toolCallId, updates) {
|
|
@@ -9696,36 +9675,109 @@ function appendPendingToolCallArgs(pendingToolCalls = [], payload) {
|
|
|
9696
9675
|
function removePendingToolCall(pendingToolCalls = [], toolCallId) {
|
|
9697
9676
|
return pendingToolCalls.filter((call) => call.toolCallId !== toolCallId);
|
|
9698
9677
|
}
|
|
9678
|
+
function createAgentResponseState() {
|
|
9679
|
+
return {
|
|
9680
|
+
id: "",
|
|
9681
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
9682
|
+
modelId: "",
|
|
9683
|
+
messages: []
|
|
9684
|
+
};
|
|
9685
|
+
}
|
|
9686
|
+
function createAgentRunState(id = "") {
|
|
9687
|
+
return {
|
|
9688
|
+
id,
|
|
9689
|
+
object: null,
|
|
9690
|
+
finishReason: null,
|
|
9691
|
+
usage: null,
|
|
9692
|
+
warnings: [],
|
|
9693
|
+
text: "",
|
|
9694
|
+
reasoning: [],
|
|
9695
|
+
sources: [],
|
|
9696
|
+
files: [],
|
|
9697
|
+
toolCalls: [],
|
|
9698
|
+
pendingToolCalls: [],
|
|
9699
|
+
toolResults: [],
|
|
9700
|
+
request: {},
|
|
9701
|
+
response: createAgentResponseState(),
|
|
9702
|
+
providerMetadata: void 0,
|
|
9703
|
+
steps: [],
|
|
9704
|
+
status: "running"
|
|
9705
|
+
};
|
|
9706
|
+
}
|
|
9707
|
+
function cloneAgentResponse(response, { includeMessages }) {
|
|
9708
|
+
if (!response) return response;
|
|
9709
|
+
return {
|
|
9710
|
+
...response,
|
|
9711
|
+
...Object.prototype.hasOwnProperty.call(response, "messages") ? { messages: includeMessages ? response.messages : [] } : {},
|
|
9712
|
+
...Object.prototype.hasOwnProperty.call(response, "dbMessages") ? { dbMessages: includeMessages ? response.dbMessages : [] } : {},
|
|
9713
|
+
...Object.prototype.hasOwnProperty.call(response, "uiMessages") ? { uiMessages: includeMessages ? response.uiMessages : [] } : {}
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
function cloneAgentStep(step, { includeDetails }) {
|
|
9717
|
+
if (includeDetails) return {
|
|
9718
|
+
...step,
|
|
9719
|
+
response: cloneAgentResponse(step.response, { includeMessages: true })
|
|
9720
|
+
};
|
|
9721
|
+
return {
|
|
9722
|
+
...step,
|
|
9723
|
+
object: null,
|
|
9724
|
+
files: [],
|
|
9725
|
+
sources: [],
|
|
9726
|
+
toolCalls: [],
|
|
9727
|
+
pendingToolCalls: [],
|
|
9728
|
+
toolResults: [],
|
|
9729
|
+
dynamicToolCalls: [],
|
|
9730
|
+
dynamicToolResults: [],
|
|
9731
|
+
staticToolCalls: [],
|
|
9732
|
+
staticToolResults: [],
|
|
9733
|
+
text: "",
|
|
9734
|
+
reasoning: [],
|
|
9735
|
+
content: Array.isArray(step.content) ? [] : step.content,
|
|
9736
|
+
reasoningText: typeof step.reasoningText === "string" ? "" : step.reasoningText,
|
|
9737
|
+
response: cloneAgentResponse(step.response, { includeMessages: false })
|
|
9738
|
+
};
|
|
9739
|
+
}
|
|
9740
|
+
function serializeAgentRun(current, { includeCompletedStepDetails, includeResponseMessages }) {
|
|
9741
|
+
const { _textOffset: _to, _reasoningOffset: _ro, ...data } = current;
|
|
9742
|
+
return {
|
|
9743
|
+
...data,
|
|
9744
|
+
response: cloneAgentResponse(data.response, { includeMessages: includeResponseMessages }),
|
|
9745
|
+
steps: data.steps.map((step) => cloneAgentStep(step, { includeDetails: includeCompletedStepDetails }))
|
|
9746
|
+
};
|
|
9747
|
+
}
|
|
9748
|
+
function createAgentDataPart(args) {
|
|
9749
|
+
const { current, runId, includeCompletedStepDetails, includeResponseMessages } = args;
|
|
9750
|
+
return {
|
|
9751
|
+
type: "data-tool-agent",
|
|
9752
|
+
id: runId,
|
|
9753
|
+
data: serializeAgentRun(current, {
|
|
9754
|
+
includeCompletedStepDetails,
|
|
9755
|
+
includeResponseMessages
|
|
9756
|
+
})
|
|
9757
|
+
};
|
|
9758
|
+
}
|
|
9759
|
+
function createAgentStepDataPart(args) {
|
|
9760
|
+
const { runId, stepIndex, step } = args;
|
|
9761
|
+
return {
|
|
9762
|
+
type: "data-tool-agent-step",
|
|
9763
|
+
id: `${runId}:${stepIndex}`,
|
|
9764
|
+
data: {
|
|
9765
|
+
runId,
|
|
9766
|
+
stepIndex,
|
|
9767
|
+
step: cloneAgentStep(step, { includeDetails: true })
|
|
9768
|
+
}
|
|
9769
|
+
};
|
|
9770
|
+
}
|
|
9699
9771
|
function transformAgent(payload, bufferedSteps) {
|
|
9700
9772
|
let hasChanged = false;
|
|
9773
|
+
let completedStep = null;
|
|
9701
9774
|
switch (payload.type) {
|
|
9702
|
-
case "start":
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
object: null,
|
|
9706
|
-
finishReason: null,
|
|
9707
|
-
usage: null,
|
|
9708
|
-
warnings: [],
|
|
9709
|
-
text: "",
|
|
9710
|
-
reasoning: [],
|
|
9711
|
-
sources: [],
|
|
9712
|
-
files: [],
|
|
9713
|
-
toolCalls: [],
|
|
9714
|
-
pendingToolCalls: [],
|
|
9715
|
-
toolResults: [],
|
|
9716
|
-
request: {},
|
|
9717
|
-
response: {
|
|
9718
|
-
id: "",
|
|
9719
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
9720
|
-
modelId: "",
|
|
9721
|
-
messages: []
|
|
9722
|
-
},
|
|
9723
|
-
providerMetadata: void 0,
|
|
9724
|
-
steps: [],
|
|
9725
|
-
status: "running"
|
|
9726
|
-
});
|
|
9775
|
+
case "start": {
|
|
9776
|
+
const startState = createAgentRunState(payload.payload.id);
|
|
9777
|
+
bufferedSteps.set(payload.runId, startState);
|
|
9727
9778
|
hasChanged = true;
|
|
9728
9779
|
break;
|
|
9780
|
+
}
|
|
9729
9781
|
case "tool-call-input-streaming-start": {
|
|
9730
9782
|
const toolInputStartRun = ensureAgentRunState(bufferedSteps, payload.runId);
|
|
9731
9783
|
const existing = toolInputStartRun.pendingToolCalls?.find((call) => call.toolCallId === payload.payload.toolCallId);
|
|
@@ -9856,6 +9908,7 @@ function transformAgent(payload, bufferedSteps) {
|
|
|
9856
9908
|
break;
|
|
9857
9909
|
case "step-finish": {
|
|
9858
9910
|
const stepRun = ensureAgentRunState(bufferedSteps, payload.runId);
|
|
9911
|
+
const stepIndex = stepRun.steps.length;
|
|
9859
9912
|
const { steps: _steps, _textOffset, _reasoningOffset, ...stepRunWithoutSteps } = stepRun;
|
|
9860
9913
|
const textOffset = _textOffset || 0;
|
|
9861
9914
|
const reasoningOffset = _reasoningOffset || 0;
|
|
@@ -9897,18 +9950,28 @@ function transformAgent(payload, bufferedSteps) {
|
|
|
9897
9950
|
_textOffset: stepRun.text.length,
|
|
9898
9951
|
_reasoningOffset: stepRun.reasoning.length
|
|
9899
9952
|
});
|
|
9953
|
+
completedStep = {
|
|
9954
|
+
stepIndex,
|
|
9955
|
+
step: stepResult
|
|
9956
|
+
};
|
|
9900
9957
|
hasChanged = true;
|
|
9901
9958
|
break;
|
|
9902
9959
|
}
|
|
9903
9960
|
default: break;
|
|
9904
9961
|
}
|
|
9905
9962
|
if (hasChanged) {
|
|
9906
|
-
const
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
};
|
|
9963
|
+
const snapshot = createAgentDataPart({
|
|
9964
|
+
current: bufferedSteps.get(payload.runId),
|
|
9965
|
+
runId: payload.runId,
|
|
9966
|
+
includeCompletedStepDetails: payload.type === "finish",
|
|
9967
|
+
includeResponseMessages: payload.type === "finish"
|
|
9968
|
+
});
|
|
9969
|
+
if (completedStep) return [snapshot, createAgentStepDataPart({
|
|
9970
|
+
runId: payload.runId,
|
|
9971
|
+
stepIndex: completedStep.stepIndex,
|
|
9972
|
+
step: completedStep.step
|
|
9973
|
+
})];
|
|
9974
|
+
return snapshot;
|
|
9912
9975
|
}
|
|
9913
9976
|
return null;
|
|
9914
9977
|
}
|
|
@@ -10334,10 +10397,24 @@ function transformNetwork(payload, bufferedNetworks, isNested) {
|
|
|
10334
10397
|
const step = current.steps.find((step) => step.id === stepId);
|
|
10335
10398
|
if (!step) return null;
|
|
10336
10399
|
step[PRIMITIVE_CACHE_SYMBOL] = step[PRIMITIVE_CACHE_SYMBOL] || /* @__PURE__ */ new Map();
|
|
10400
|
+
if (payload.payload.type === "start") delete step[COMPLETED_STEPS_SYMBOL];
|
|
10337
10401
|
const result = transformAgent(payload.payload, step[PRIMITIVE_CACHE_SYMBOL]);
|
|
10338
|
-
|
|
10339
|
-
|
|
10402
|
+
const snapshot = Array.isArray(result) ? result[0] : result;
|
|
10403
|
+
if (snapshot) {
|
|
10404
|
+
const { request, response, ...data } = snapshot.data;
|
|
10405
|
+
if (Array.isArray(result)) {
|
|
10406
|
+
const { stepIndex, step: completedStepDetail } = result[1].data;
|
|
10407
|
+
step[COMPLETED_STEPS_SYMBOL] = step[COMPLETED_STEPS_SYMBOL] || /* @__PURE__ */ new Map();
|
|
10408
|
+
step[COMPLETED_STEPS_SYMBOL].set(stepIndex, completedStepDetail);
|
|
10409
|
+
}
|
|
10340
10410
|
step.task = data;
|
|
10411
|
+
const completedSteps = step[COMPLETED_STEPS_SYMBOL];
|
|
10412
|
+
if (completedSteps && completedSteps.size > 0 && Array.isArray(data.steps)) {
|
|
10413
|
+
for (const [stepIndex, completedStepDetail] of completedSteps) if (stepIndex < data.steps.length) data.steps[stepIndex] = {
|
|
10414
|
+
...data.steps[stepIndex],
|
|
10415
|
+
...completedStepDetail
|
|
10416
|
+
};
|
|
10417
|
+
}
|
|
10341
10418
|
}
|
|
10342
10419
|
bufferedNetworks.set(payload.runId, current);
|
|
10343
10420
|
return {
|
|
@@ -10461,6 +10538,124 @@ function toAISdkStream(stream, options = {
|
|
|
10461
10538
|
}));
|
|
10462
10539
|
}
|
|
10463
10540
|
//#endregion
|
|
10541
|
+
//#region src/sse-heartbeat.ts
|
|
10542
|
+
const SSE_HEARTBEAT_BYTES = new TextEncoder().encode(": heartbeat\n\n");
|
|
10543
|
+
const LF_BYTE = 10;
|
|
10544
|
+
const MAX_TIMEOUT_MS = 2147483647;
|
|
10545
|
+
/** Throws when an enabled heartbeat interval cannot be scheduled with a timer. */
|
|
10546
|
+
function assertValidHeartbeatMs(heartbeatMs) {
|
|
10547
|
+
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}`);
|
|
10548
|
+
}
|
|
10549
|
+
/**
|
|
10550
|
+
* Adds periodic SSE comment heartbeats to an AI SDK response body.
|
|
10551
|
+
* AI SDK serialization uses LF-delimited frames, which this private wrapper preserves and relies on.
|
|
10552
|
+
*/
|
|
10553
|
+
function withSseHeartbeat(response, heartbeatMs) {
|
|
10554
|
+
assertValidHeartbeatMs(heartbeatMs);
|
|
10555
|
+
if (heartbeatMs === void 0 || heartbeatMs <= 0 || !response.body) return response;
|
|
10556
|
+
const reader = response.body.getReader();
|
|
10557
|
+
let heartbeatTimeout;
|
|
10558
|
+
let wakePull;
|
|
10559
|
+
let readResult;
|
|
10560
|
+
let readError;
|
|
10561
|
+
let hasReadError = false;
|
|
10562
|
+
let reading = false;
|
|
10563
|
+
let finished = false;
|
|
10564
|
+
let readerReleased = false;
|
|
10565
|
+
let atFrameBoundary = true;
|
|
10566
|
+
let lastByte;
|
|
10567
|
+
let nextHeartbeatAt = performance.now() + heartbeatMs;
|
|
10568
|
+
const clearHeartbeat = () => {
|
|
10569
|
+
if (heartbeatTimeout !== void 0) {
|
|
10570
|
+
clearTimeout(heartbeatTimeout);
|
|
10571
|
+
heartbeatTimeout = void 0;
|
|
10572
|
+
}
|
|
10573
|
+
};
|
|
10574
|
+
const releaseReader = () => {
|
|
10575
|
+
if (readerReleased) return;
|
|
10576
|
+
readerReleased = true;
|
|
10577
|
+
reader.releaseLock();
|
|
10578
|
+
};
|
|
10579
|
+
const updateFrameBoundary = (chunk) => {
|
|
10580
|
+
if (chunk.byteLength === 0) return;
|
|
10581
|
+
atFrameBoundary = chunk.byteLength === 1 ? lastByte === LF_BYTE && chunk[0] === LF_BYTE : chunk[chunk.byteLength - 2] === LF_BYTE && chunk[chunk.byteLength - 1] === LF_BYTE;
|
|
10582
|
+
lastByte = chunk[chunk.byteLength - 1];
|
|
10583
|
+
};
|
|
10584
|
+
const startRead = () => {
|
|
10585
|
+
if (reading || readResult || hasReadError || finished) return;
|
|
10586
|
+
reading = true;
|
|
10587
|
+
reader.read().then((result) => {
|
|
10588
|
+
reading = false;
|
|
10589
|
+
readResult = result;
|
|
10590
|
+
wakePull?.("read");
|
|
10591
|
+
}, (error) => {
|
|
10592
|
+
reading = false;
|
|
10593
|
+
readError = error;
|
|
10594
|
+
hasReadError = true;
|
|
10595
|
+
wakePull?.("read");
|
|
10596
|
+
});
|
|
10597
|
+
};
|
|
10598
|
+
const forwardRead = (controller) => {
|
|
10599
|
+
if (hasReadError) {
|
|
10600
|
+
finished = true;
|
|
10601
|
+
releaseReader();
|
|
10602
|
+
controller.error(readError);
|
|
10603
|
+
return;
|
|
10604
|
+
}
|
|
10605
|
+
if (!readResult) return;
|
|
10606
|
+
const result = readResult;
|
|
10607
|
+
readResult = void 0;
|
|
10608
|
+
if (result.done) {
|
|
10609
|
+
finished = true;
|
|
10610
|
+
releaseReader();
|
|
10611
|
+
controller.close();
|
|
10612
|
+
return;
|
|
10613
|
+
}
|
|
10614
|
+
updateFrameBoundary(result.value);
|
|
10615
|
+
controller.enqueue(result.value);
|
|
10616
|
+
};
|
|
10617
|
+
const stream = new ReadableStream({
|
|
10618
|
+
async pull(controller) {
|
|
10619
|
+
if (finished) return;
|
|
10620
|
+
startRead();
|
|
10621
|
+
if (readResult || hasReadError) {
|
|
10622
|
+
forwardRead(controller);
|
|
10623
|
+
return;
|
|
10624
|
+
}
|
|
10625
|
+
const next = await new Promise((resolve) => {
|
|
10626
|
+
wakePull = resolve;
|
|
10627
|
+
if (atFrameBoundary) heartbeatTimeout = setTimeout(() => resolve("heartbeat"), Math.max(0, nextHeartbeatAt - performance.now()));
|
|
10628
|
+
});
|
|
10629
|
+
wakePull = void 0;
|
|
10630
|
+
clearHeartbeat();
|
|
10631
|
+
if (finished) return;
|
|
10632
|
+
if (readResult || hasReadError) {
|
|
10633
|
+
forwardRead(controller);
|
|
10634
|
+
return;
|
|
10635
|
+
}
|
|
10636
|
+
if (next === "heartbeat") {
|
|
10637
|
+
controller.enqueue(SSE_HEARTBEAT_BYTES.slice());
|
|
10638
|
+
nextHeartbeatAt = performance.now() + heartbeatMs;
|
|
10639
|
+
}
|
|
10640
|
+
},
|
|
10641
|
+
async cancel(reason) {
|
|
10642
|
+
if (finished) return;
|
|
10643
|
+
finished = true;
|
|
10644
|
+
clearHeartbeat();
|
|
10645
|
+
try {
|
|
10646
|
+
await reader.cancel(reason);
|
|
10647
|
+
} finally {
|
|
10648
|
+
releaseReader();
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
});
|
|
10652
|
+
return new Response(stream, {
|
|
10653
|
+
status: response.status,
|
|
10654
|
+
statusText: response.statusText,
|
|
10655
|
+
headers: response.headers
|
|
10656
|
+
});
|
|
10657
|
+
}
|
|
10658
|
+
//#endregion
|
|
10464
10659
|
//#region src/chat-route.ts
|
|
10465
10660
|
/**
|
|
10466
10661
|
* Collects every approval response across all assistant messages in a v6
|
|
@@ -10663,11 +10858,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
|
|
|
10663
10858
|
* @param {boolean} [options.sendFinish=true] - Whether to send finish events in the stream
|
|
10664
10859
|
* @param {boolean} [options.sendReasoning=false] - Whether to include reasoning steps in the stream
|
|
10665
10860
|
* @param {boolean} [options.sendSources=false] - Whether to include source citations in the stream
|
|
10861
|
+
* @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`.
|
|
10666
10862
|
* @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.
|
|
10667
10863
|
*
|
|
10668
10864
|
* @returns {ReturnType<typeof registerApiRoute>} A registered API route handler
|
|
10669
10865
|
*
|
|
10670
10866
|
* @throws {Error} When path doesn't include `:agentId` and no fixed agent is specified
|
|
10867
|
+
* @throws {RangeError} When `heartbeatMs` is `NaN`, positive infinity, or greater than 2,147,483,647
|
|
10671
10868
|
* @throws {Error} When agent ID is missing at runtime
|
|
10672
10869
|
* @throws {Error} When specified agent is not found in Mastra instance
|
|
10673
10870
|
*
|
|
@@ -10690,12 +10887,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
|
|
|
10690
10887
|
* @remarks
|
|
10691
10888
|
* - The route handler expects a JSON body with a `messages` array
|
|
10692
10889
|
* - Messages should follow the format: `{ role: 'user' | 'assistant' | 'system', content: string }`
|
|
10693
|
-
* - The response is a Server-Sent Events (SSE) stream compatible with AI SDK
|
|
10890
|
+
* - The response is a Server-Sent Events (SSE) stream compatible with the selected AI SDK version
|
|
10694
10891
|
* - If both `agent` and `:agentId` are present, a warning is logged and the fixed `agent` takes precedence
|
|
10695
10892
|
* - Request context from the incoming request overrides `defaultOptions.requestContext` if both are present
|
|
10696
10893
|
*/
|
|
10697
|
-
function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, onError }) {
|
|
10894
|
+
function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, heartbeatMs, onError }) {
|
|
10698
10895
|
if (!agent && !path.includes("/:agentId")) throw new Error("Path must include :agentId to route to the correct agent or pass the agent explicitly");
|
|
10896
|
+
assertValidHeartbeatMs(heartbeatMs);
|
|
10699
10897
|
return (0, _mastra_core_server.registerApiRoute)(path, {
|
|
10700
10898
|
method: "POST",
|
|
10701
10899
|
openapi: {
|
|
@@ -10824,11 +11022,13 @@ function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimenta
|
|
|
10824
11022
|
sendSources,
|
|
10825
11023
|
onError
|
|
10826
11024
|
};
|
|
10827
|
-
|
|
11025
|
+
let response;
|
|
11026
|
+
if (version === "v6") response = createUIMessageStreamResponse({ stream: await handleChatStream({
|
|
10828
11027
|
...handlerOptions,
|
|
10829
11028
|
version: "v6"
|
|
10830
11029
|
}) });
|
|
10831
|
-
|
|
11030
|
+
else response = createUIMessageStreamResponse$1({ stream: await handleChatStream(handlerOptions) });
|
|
11031
|
+
return withSseHeartbeat(response, heartbeatMs);
|
|
10832
11032
|
}
|
|
10833
11033
|
});
|
|
10834
11034
|
}
|