@mastra/mcp-docs-server 1.2.7-alpha.3 → 1.2.7-alpha.4
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/.docs/docs/voice/livekit.md +100 -3
- package/.docs/models/environment-variables.md +1 -0
- package/.docs/models/gateways/openrouter.md +3 -1
- package/.docs/models/gateways/vercel.md +3 -1
- package/.docs/models/index.md +1 -1
- package/.docs/models/providers/google.md +2 -1
- package/.docs/models/providers/llmgateway.md +4 -4
- package/.docs/models/providers/opencode.md +2 -1
- package/.docs/models/providers/wandb.md +21 -14
- package/.docs/models/providers/xai.md +2 -1
- package/.docs/models/providers/zenifra.md +73 -0
- package/.docs/models/providers.md +1 -0
- package/.docs/reference/index.md +2 -0
- package/.docs/reference/memory/summarizeConversation.md +99 -0
- package/.docs/reference/memory/summarizeThread.md +93 -0
- package/.docs/reference/voice/livekit.md +272 -8
- package/package.json +3 -3
|
@@ -162,6 +162,45 @@ export default createLiveKitWorker({
|
|
|
162
162
|
|
|
163
163
|
See the [LiveKit turn detection docs](https://docs.livekit.io/agents/logic/turns/) for all options.
|
|
164
164
|
|
|
165
|
+
## Per-call voices and transcription
|
|
166
|
+
|
|
167
|
+
The top-level `stt` and `tts` options apply to every call. To pick them per call — one voice or language per tenant — set the `configuration.stt` and `configuration.tts` resolvers instead. Each resolver runs once per call with the dispatch metadata, request context, room name, and job context, and returns anything the matching top-level option accepts: a plugin instance or an inference model string. Return `undefined` to fall back to the top-level option.
|
|
168
|
+
|
|
169
|
+
The following example gives each tenant its own text-to-speech voice, keyed off the `tenant` entry in the dispatch metadata:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import * as cartesia from '@livekit/agents-plugin-cartesia'
|
|
173
|
+
|
|
174
|
+
// One voice id per tenant, resolved from the dispatch metadata on each call.
|
|
175
|
+
const tenantVoices: Record<string, string> = {
|
|
176
|
+
meridian: 'your-cartesia-voice-id-1',
|
|
177
|
+
coastal: 'your-cartesia-voice-id-2',
|
|
178
|
+
}
|
|
179
|
+
// The resolver runs during call setup, so cache plugin instances across calls.
|
|
180
|
+
const ttsByVoice = new Map<string, cartesia.TTS>()
|
|
181
|
+
|
|
182
|
+
export default createLiveKitWorker({
|
|
183
|
+
mastra,
|
|
184
|
+
agent: 'support',
|
|
185
|
+
stt: 'deepgram/nova-3',
|
|
186
|
+
tts: 'cartesia/sonic-3',
|
|
187
|
+
configuration: {
|
|
188
|
+
tts: ({ requestContext }) => {
|
|
189
|
+
const voice = tenantVoices[requestContext?.tenant as string]
|
|
190
|
+
if (!voice) return undefined // fall back to the top-level `tts`
|
|
191
|
+
let tts = ttsByVoice.get(voice)
|
|
192
|
+
if (!tts) {
|
|
193
|
+
tts = new cartesia.TTS({ voice })
|
|
194
|
+
ttsByVoice.set(voice, tts)
|
|
195
|
+
}
|
|
196
|
+
return tts
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`configuration.stt` works the same way for per-call transcription, for example a different transcription model or language per tenant. The greeting has a matching per-call form: `configuration.greeting.text` accepts a resolver with the same call context, so one worker can open with each tenant's own phrasing.
|
|
203
|
+
|
|
165
204
|
## Memory and threads
|
|
166
205
|
|
|
167
206
|
When the resolved Mastra agent has memory configured, each call becomes one memory thread:
|
|
@@ -172,7 +211,7 @@ When the resolved Mastra agent has memory configured, each call becomes one memo
|
|
|
172
211
|
|
|
173
212
|
Each turn sends only the new user input; Mastra Memory supplies history, semantic recall, and working memory. Pin a session to an existing thread by passing `threadId` in the connection request body, which is useful for continuing a text conversation by voice. In Studio, starting a call from an open chat binds the call to that thread, and the transcript fills into the chat after each exchange.
|
|
174
213
|
|
|
175
|
-
|
|
214
|
+
When a user interrupts the agent, the in-flight generation aborts and nothing from that turn is persisted at that moment. LiveKit keeps the part the user actually heard in its transcript, and on the next turn the worker re-sends that heard-only fragment so the thread backfills to match the call. A user who hangs up right after interrupting leaves that final fragment unrecorded; see [interrupted turns](https://mastra.ai/reference/voice/livekit) for the details and a reconciliation recipe.
|
|
176
215
|
|
|
177
216
|
## Speak while tools run
|
|
178
217
|
|
|
@@ -189,13 +228,13 @@ export default createLiveKitWorker({
|
|
|
189
228
|
})
|
|
190
229
|
```
|
|
191
230
|
|
|
192
|
-
The phrase is spoken as part of the reply and
|
|
231
|
+
The phrase is spoken as part of the reply and recorded in the transcript.
|
|
193
232
|
|
|
194
233
|
## Generate replies with a workflow
|
|
195
234
|
|
|
196
235
|
By default the worker generates each reply with a Mastra agent. To run multi-step logic per turn (for example classify intent, route, call tools in sequence, then compose a reply), generate replies with a Mastra [workflow](https://mastra.ai/docs/workflows/overview) instead. Set `workflow` in place of `agent`.
|
|
197
236
|
|
|
198
|
-
LiveKit still owns the audio loop and calls into Mastra once per turn, so the workflow runs to completion each turn.
|
|
237
|
+
LiveKit still owns the audio loop and calls into Mastra once per turn, so the workflow runs to completion each turn. The workflow can't suspend or resume, and no conversation state carries between turns. Pass the transcript in through `workflowInput` so the workflow stays stateless:
|
|
199
238
|
|
|
200
239
|
```typescript
|
|
201
240
|
import { createLiveKitWorker, chatContextToMessages } from '@mastra/livekit/worker'
|
|
@@ -233,6 +272,64 @@ const generateResponse = createStep({
|
|
|
233
272
|
|
|
234
273
|
With a workflow, the worker doesn't persist turns automatically the way an agent's `stream()` does. Persist conversation history inside the workflow, or keep the LiveKit transcript as the source of truth and pass it in each turn.
|
|
235
274
|
|
|
275
|
+
## Use Mastra as the LLM component
|
|
276
|
+
|
|
277
|
+
`createLiveKitWorker()` owns the LiveKit session for you. To own the session yourself, use [`MastraLLM`](https://mastra.ai/reference/voice/livekit) instead: a standard LiveKit LLM plugin that puts a Mastra agent in the `llm` slot of your own `voice.AgentSession`. The Mastra app — agent loop, tools, memory, observability — runs on your Mastra server, and the worker reaches it over HTTP. The worker process needs no Mastra app, database, or model provider keys.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { fileURLToPath } from 'node:url'
|
|
281
|
+
import { defineAgent, voice } from '@livekit/agents'
|
|
282
|
+
import * as silero from '@livekit/agents-plugin-silero'
|
|
283
|
+
import { MastraLLM } from '@mastra/livekit/plugin'
|
|
284
|
+
import { runLiveKitWorker } from '@mastra/livekit/worker'
|
|
285
|
+
|
|
286
|
+
export default defineAgent({
|
|
287
|
+
entry: async ctx => {
|
|
288
|
+
await ctx.connect()
|
|
289
|
+
|
|
290
|
+
const session = new voice.AgentSession({
|
|
291
|
+
llm: new MastraLLM({
|
|
292
|
+
remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'support' },
|
|
293
|
+
memory: { thread: ctx.room.name!, resource: 'user-7' },
|
|
294
|
+
}),
|
|
295
|
+
stt: 'deepgram/nova-3',
|
|
296
|
+
tts: 'cartesia/sonic-3',
|
|
297
|
+
vad: await silero.VAD.load(),
|
|
298
|
+
// Required with `memory`: LiveKit enables preemptive generation by default.
|
|
299
|
+
turnHandling: { preemptiveGeneration: { enabled: false } },
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
await session.start({
|
|
303
|
+
// These instructions never reach the Mastra agent; its own instructions apply.
|
|
304
|
+
agent: new voice.Agent({ instructions: 'Replies come from the Mastra agent.' }),
|
|
305
|
+
room: ctx.room,
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
session.say('Hi! How can I help you today?')
|
|
309
|
+
},
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
313
|
+
runLiveKitWorker({ entry: import.meta.url, agentName: 'mastra-voice' })
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Both paths share the same reply pipeline underneath; choose by who should own the session:
|
|
318
|
+
|
|
319
|
+
| | `createLiveKitWorker()` | `MastraLLM` |
|
|
320
|
+
| ------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
|
|
321
|
+
| Session ownership | The worker helper builds and manages the `AgentSession` | Your code builds the session; every LiveKit option and hook is yours |
|
|
322
|
+
| Where the Mastra app runs | In the worker process | On your Mastra server, reached over HTTP (or in-process via `agent`) |
|
|
323
|
+
| Worker process needs | Your Mastra app, storage, and model provider keys | Only the LiveKit SDK and network access to your server |
|
|
324
|
+
| Built-in conveniences | Greeting, consent gating, agent-initiated hang-up, thread bootstrap, observability roll-up | Rebuild what you need with the [session helpers](https://mastra.ai/reference/voice/livekit) |
|
|
325
|
+
| Best for | Fastest path to a working voice agent; Studio voice mode | Existing LiveKit apps and full control over the session |
|
|
326
|
+
|
|
327
|
+
Tools stay on the Mastra agent and execute on the server; LiveKit-side tools passed to the session are ignored. Tool activity reaches the worker through `toolFeedback` (spoken filler), `onToolCall` (fires as each tool call starts), and `onTurnComplete` (fires after each reply with the text, tool calls, and token usage). Agent-initiated hang-up takes a few lines: pair `onToolCall` with [`runEndCall()`](https://mastra.ai/reference/voice/livekit).
|
|
328
|
+
|
|
329
|
+
> **Warning:** Don't combine the `memory` option with LiveKit's `preemptiveGeneration`, which LiveKit enables by default in sessions you build yourself. A speculative turn that completes before LiveKit discards it persists a user message and a never-spoken reply to the thread. Set `turnHandling: { preemptiveGeneration: { enabled: false } }` as shown above, or run without `memory` and pass the full transcript each turn.
|
|
330
|
+
|
|
331
|
+
`MastraLLM` also accepts an in-process Mastra `agent` instance — session ownership without a second deployment — or a custom `generate` function. The remote transport is available standalone as [`createRemoteAgentReplyGenerator()`](https://mastra.ai/reference/voice/livekit), which also plugs into `createLiveKitWorker`'s `generate` option to run the batteries-included worker against a remote server.
|
|
332
|
+
|
|
236
333
|
## Server-initiated sessions
|
|
237
334
|
|
|
238
335
|
Use `dispatchVoiceSession()` to add a voice agent to a room from your own code, for example to join an existing room or to drive an outbound [SIP call](https://docs.livekit.io/sip/):
|
|
@@ -138,6 +138,7 @@ List of required environment variables for each model provider and gateway suppo
|
|
|
138
138
|
| [Z.AI](https://mastra.ai/models/providers/zai) | `zai/*` | `ZHIPU_API_KEY` |
|
|
139
139
|
| [Z.AI Coding Plan](https://mastra.ai/models/providers/zai-coding-plan) | `zai-coding-plan/*` | `ZHIPU_API_KEY` |
|
|
140
140
|
| [Zeldoc](https://mastra.ai/models/providers/zeldoc) | `zeldoc/*` | `ZELDOC_API_KEY` |
|
|
141
|
+
| [Zenifra](https://mastra.ai/models/providers/zenifra) | `zenifra/*` | `ZENIFRA_AI_KEY` |
|
|
141
142
|
| [ZenMux](https://mastra.ai/models/providers/zenmux) | `zenmux/*` | `ZENMUX_API_KEY` |
|
|
142
143
|
| [Zhipu AI](https://mastra.ai/models/providers/zhipuai) | `zhipuai/*` | `ZHIPU_API_KEY` |
|
|
143
144
|
| [Zhipu AI Coding Plan](https://mastra.ai/models/providers/zhipuai-coding-plan) | `zhipuai-coding-plan/*` | `ZHIPU_API_KEY` |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# OpenRouter
|
|
4
4
|
|
|
5
|
-
OpenRouter aggregates models from multiple providers with enhanced features like rate limiting and failover. Access
|
|
5
|
+
OpenRouter aggregates models from multiple providers with enhanced features like rate limiting and failover. Access 344 models through Mastra's model router.
|
|
6
6
|
|
|
7
7
|
Learn more in the [OpenRouter documentation](https://openrouter.ai/models).
|
|
8
8
|
|
|
@@ -45,6 +45,7 @@ ANTHROPIC_API_KEY=ant-...
|
|
|
45
45
|
| `~moonshotai/kimi-latest` |
|
|
46
46
|
| `~openai/gpt-latest` |
|
|
47
47
|
| `~openai/gpt-mini-latest` |
|
|
48
|
+
| `~x-ai/grok-latest` |
|
|
48
49
|
| `ai21/jamba-large-1.7` |
|
|
49
50
|
| `aion-labs/aion-2.0` |
|
|
50
51
|
| `aion-labs/aion-3.0` |
|
|
@@ -363,6 +364,7 @@ ANTHROPIC_API_KEY=ant-...
|
|
|
363
364
|
| `x-ai/grok-4.20` |
|
|
364
365
|
| `x-ai/grok-4.20-multi-agent` |
|
|
365
366
|
| `x-ai/grok-4.3` |
|
|
367
|
+
| `x-ai/grok-4.5` |
|
|
366
368
|
| `x-ai/grok-build-0.1` |
|
|
367
369
|
| `xiaomi/mimo-v2.5` |
|
|
368
370
|
| `xiaomi/mimo-v2.5-pro` |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Vercel
|
|
4
4
|
|
|
5
|
-
Vercel aggregates models from multiple providers with enhanced features like rate limiting and failover. Access
|
|
5
|
+
Vercel aggregates models from multiple providers with enhanced features like rate limiting and failover. Access 301 models through Mastra's model router.
|
|
6
6
|
|
|
7
7
|
Learn more in the [Vercel documentation](https://ai-sdk.dev/providers/ai-sdk-providers).
|
|
8
8
|
|
|
@@ -141,6 +141,7 @@ ANTHROPIC_API_KEY=ant-...
|
|
|
141
141
|
| `google/gemini-3.5-flash` |
|
|
142
142
|
| `google/gemini-embedding-001` |
|
|
143
143
|
| `google/gemini-embedding-2` |
|
|
144
|
+
| `google/gemini-omni-flash-preview` |
|
|
144
145
|
| `google/gemma-4-26b-a4b-it` |
|
|
145
146
|
| `google/gemma-4-31b-it` |
|
|
146
147
|
| `google/imagen-4.0-fast-generate-001` |
|
|
@@ -308,6 +309,7 @@ ANTHROPIC_API_KEY=ant-...
|
|
|
308
309
|
| `xai/grok-4.20-reasoning` |
|
|
309
310
|
| `xai/grok-4.20-reasoning-beta` |
|
|
310
311
|
| `xai/grok-4.3` |
|
|
312
|
+
| `xai/grok-4.5` |
|
|
311
313
|
| `xai/grok-build-0.1` |
|
|
312
314
|
| `xai/grok-imagine-image` |
|
|
313
315
|
| `xai/grok-imagine-video` |
|
package/.docs/models/index.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Model Providers
|
|
4
4
|
|
|
5
|
-
Mastra provides a unified interface for working with LLMs across multiple providers, giving you access to
|
|
5
|
+
Mastra provides a unified interface for working with LLMs across multiple providers, giving you access to 4582 models from 141 providers through a single API.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Google
|
|
4
4
|
|
|
5
|
-
Access
|
|
5
|
+
Access 19 Google models through Mastra's model router. Authentication is handled automatically using one of the following environment variables: `GOOGLE_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`.
|
|
6
6
|
|
|
7
7
|
Learn more in the [Google documentation](https://ai.google.dev/gemini-api/docs/models).
|
|
8
8
|
|
|
@@ -51,6 +51,7 @@ for await (const chunk of stream) {
|
|
|
51
51
|
| `google/gemini-embedding-001` | 2K | | | | | | $0.15 | — |
|
|
52
52
|
| `google/gemini-flash-latest` | 1.0M | | | | | | $0.30 | $3 |
|
|
53
53
|
| `google/gemini-flash-lite-latest` | 1.0M | | | | | | $0.10 | $0.40 |
|
|
54
|
+
| `google/gemini-omni-flash-preview` | 131K | | | | | | $2 | $18 |
|
|
54
55
|
| `google/gemma-4-26b-a4b-it` | 262K | | | | | | — | — |
|
|
55
56
|
| `google/gemma-4-31b-it` | 262K | | | | | | — | — |
|
|
56
57
|
|
|
@@ -75,7 +75,7 @@ for await (const chunk of stream) {
|
|
|
75
75
|
| `llmgateway/glm-4.5-airx` | 128K | | | | | | $1 | $5 |
|
|
76
76
|
| `llmgateway/glm-4.5-x` | 128K | | | | | | $2 | $9 |
|
|
77
77
|
| `llmgateway/glm-4.5v` | 128K | | | | | | $0.60 | $2 |
|
|
78
|
-
| `llmgateway/glm-4.6` | 205K | | | | | | $0.
|
|
78
|
+
| `llmgateway/glm-4.6` | 205K | | | | | | $0.55 | $2 |
|
|
79
79
|
| `llmgateway/glm-4.6v` | 131K | | | | | | $0.30 | $0.90 |
|
|
80
80
|
| `llmgateway/glm-4.6v-flashx` | 128K | | | | | | $0.04 | $0.40 |
|
|
81
81
|
| `llmgateway/glm-4.7` | 205K | | | | | | $0.38 | $2 |
|
|
@@ -124,9 +124,10 @@ for await (const chunk of stream) {
|
|
|
124
124
|
| `llmgateway/grok-4-20-non-reasoning` | 2.0M | | | | | | $1 | $3 |
|
|
125
125
|
| `llmgateway/grok-4-20-reasoning` | 2.0M | | | | | | $1 | $3 |
|
|
126
126
|
| `llmgateway/grok-4-3` | 1.0M | | | | | | $1 | $3 |
|
|
127
|
+
| `llmgateway/grok-4-5` | 500K | | | | | | $2 | $6 |
|
|
127
128
|
| `llmgateway/grok-build-0-1` | 256K | | | | | | $1 | $2 |
|
|
128
129
|
| `llmgateway/kimi-k2` | 256K | | | | | | $0.57 | $2 |
|
|
129
|
-
| `llmgateway/kimi-k2-thinking` | 262K | | | | | | $0.
|
|
130
|
+
| `llmgateway/kimi-k2-thinking` | 262K | | | | | | $0.60 | $3 |
|
|
130
131
|
| `llmgateway/kimi-k2.5` | 262K | | | | | | $0.41 | $2 |
|
|
131
132
|
| `llmgateway/kimi-k2.6` | 262K | | | | | | $0.40 | $2 |
|
|
132
133
|
| `llmgateway/kimi-k2.7-code` | 262K | | | | | | $0.95 | $4 |
|
|
@@ -189,11 +190,10 @@ for await (const chunk of stream) {
|
|
|
189
190
|
| `llmgateway/qwen3-coder-next` | 262K | | | | | | $0.11 | $0.68 |
|
|
190
191
|
| `llmgateway/qwen3-coder-plus` | 1.0M | | | | | | $6 | $60 |
|
|
191
192
|
| `llmgateway/qwen3-max` | 262K | | | | | | $0.84 | $3 |
|
|
192
|
-
| `llmgateway/qwen3-max-2026-01-23` | 262K | | | | | | $1 | $6 |
|
|
193
193
|
| `llmgateway/qwen3-next-80b-a3b-instruct` | 131K | | | | | | $0.15 | $1 |
|
|
194
194
|
| `llmgateway/qwen3-next-80b-a3b-thinking` | 131K | | | | | | $0.15 | $1 |
|
|
195
195
|
| `llmgateway/qwen3-vl-235b-a22b-instruct` | 131K | | | | | | $0.30 | $2 |
|
|
196
|
-
| `llmgateway/qwen3-vl-235b-a22b-thinking` | 131K | | | | | | $0.
|
|
196
|
+
| `llmgateway/qwen3-vl-235b-a22b-thinking` | 131K | | | | | | $0.98 | $4 |
|
|
197
197
|
| `llmgateway/qwen3-vl-30b-a3b-instruct` | 131K | | | | | | $0.20 | $0.70 |
|
|
198
198
|
| `llmgateway/qwen3-vl-30b-a3b-thinking` | 131K | | | | | | $0.20 | $1 |
|
|
199
199
|
| `llmgateway/qwen3-vl-8b-instruct` | 131K | | | | | | $0.08 | $0.50 |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# OpenCode Zen
|
|
4
4
|
|
|
5
|
-
Access
|
|
5
|
+
Access 52 OpenCode Zen models through Mastra's model router. Authentication is handled automatically using the `OPENCODE_API_KEY` environment variable.
|
|
6
6
|
|
|
7
7
|
Learn more in the [OpenCode Zen documentation](https://opencode.ai/docs/zen).
|
|
8
8
|
|
|
@@ -74,6 +74,7 @@ for await (const chunk of stream) {
|
|
|
74
74
|
| `opencode/gpt-5.4-pro` | 1.1M | | | | | | $30 | $180 |
|
|
75
75
|
| `opencode/gpt-5.5` | 1.1M | | | | | | $5 | $30 |
|
|
76
76
|
| `opencode/gpt-5.5-pro` | 1.1M | | | | | | $30 | $180 |
|
|
77
|
+
| `opencode/grok-4.5` | 500K | | | | | | $2 | $6 |
|
|
77
78
|
| `opencode/grok-build-0.1` | 256K | | | | | | $1 | $2 |
|
|
78
79
|
| `opencode/hy3-free` | 256K | | | | | | — | — |
|
|
79
80
|
| `opencode/kimi-k2.5` | 262K | | | | | | $0.60 | $3 |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Weights & Biases
|
|
4
4
|
|
|
5
|
-
Access
|
|
5
|
+
Access 25 Weights & Biases models through Mastra's model router. Authentication is handled automatically using the `WANDB_API_KEY` environment variable.
|
|
6
6
|
|
|
7
7
|
Learn more in the [Weights & Biases documentation](https://docs.wandb.ai).
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ const agent = new Agent({
|
|
|
17
17
|
id: "my-agent",
|
|
18
18
|
name: "My Agent",
|
|
19
19
|
instructions: "You are a helpful assistant",
|
|
20
|
-
model: "wandb/
|
|
20
|
+
model: "wandb/JetBrains/Mellum2-12B-A2.5B-Instruct"
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
// Generate a response
|
|
@@ -37,23 +37,30 @@ for await (const chunk of stream) {
|
|
|
37
37
|
| Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
|
|
38
38
|
| ---------------------------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
|
|
39
39
|
| `wandb/deepseek-ai/DeepSeek-V3.1` | 161K | | | | | | $0.55 | $2 |
|
|
40
|
+
| `wandb/deepseek-ai/DeepSeek-V4-Flash` | 1.0M | | | | | | $0.14 | $0.28 |
|
|
41
|
+
| `wandb/deepseek-ai/DeepSeek-V4-Pro` | 1.0M | | | | | | $2 | $3 |
|
|
42
|
+
| `wandb/google/gemma-4-31B-it` | 262K | | | | | | $0.12 | $0.35 |
|
|
43
|
+
| `wandb/ibm-granite/granite-4.1-8b` | 131K | | | | | | $0.05 | $0.10 |
|
|
44
|
+
| `wandb/JetBrains/Mellum2-12B-A2.5B-Instruct` | 131K | | | | | | $0.05 | $0.10 |
|
|
40
45
|
| `wandb/meta-llama/Llama-3.1-70B-Instruct` | 128K | | | | | | $0.80 | $0.80 |
|
|
41
46
|
| `wandb/meta-llama/Llama-3.1-8B-Instruct` | 128K | | | | | | $0.22 | $0.22 |
|
|
42
47
|
| `wandb/meta-llama/Llama-3.3-70B-Instruct` | 128K | | | | | | $0.71 | $0.71 |
|
|
43
|
-
| `wandb/meta-llama/Llama-4-Scout-17B-16E-Instruct` | 64K | | | | | | $0.17 | $0.66 |
|
|
44
|
-
| `wandb/microsoft/Phi-4-mini-instruct` | 128K | | | | | | $0.08 | $0.35 |
|
|
45
48
|
| `wandb/MiniMaxAI/MiniMax-M2.5` | 197K | | | | | | $0.30 | $1 |
|
|
46
|
-
| `wandb/moonshotai/Kimi-K2.5` | 262K | | | | | | $0.
|
|
49
|
+
| `wandb/moonshotai/Kimi-K2.5` | 262K | | | | | | $0.60 | $3 |
|
|
50
|
+
| `wandb/moonshotai/Kimi-K2.6` | 262K | | | | | | $0.95 | $4 |
|
|
51
|
+
| `wandb/moonshotai/Kimi-K2.7-Code` | 262K | | | | | | $0.94 | $4 |
|
|
47
52
|
| `wandb/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-FP8` | 262K | | | | | | $0.20 | $0.80 |
|
|
48
|
-
| `wandb/
|
|
49
|
-
| `wandb/openai/gpt-oss-
|
|
53
|
+
| `wandb/nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B` | 262K | | | | | | $0.75 | $3 |
|
|
54
|
+
| `wandb/openai/gpt-oss-120b` | 131K | | | | | | $0.04 | $0.14 |
|
|
55
|
+
| `wandb/openai/gpt-oss-20b` | 131K | | | | | | $0.03 | $0.13 |
|
|
50
56
|
| `wandb/OpenPipe/Qwen3-14B-Instruct` | 33K | | | | | | $0.05 | $0.22 |
|
|
51
|
-
| `wandb/Qwen/Qwen3-235B-A22B-Instruct-2507` | 262K | | | | | | $0.10 | $0.10 |
|
|
52
|
-
| `wandb/Qwen/Qwen3-235B-A22B-Thinking-2507` | 262K | | | | | | $0.10 | $0.10 |
|
|
53
57
|
| `wandb/Qwen/Qwen3-30B-A3B-Instruct-2507` | 262K | | | | | | $0.10 | $0.30 |
|
|
54
58
|
| `wandb/Qwen/Qwen3-Coder-480B-A35B-Instruct` | 262K | | | | | | $1 | $2 |
|
|
55
|
-
| `wandb/
|
|
56
|
-
| `wandb/
|
|
59
|
+
| `wandb/Qwen/Qwen3.5-35B-A3B` | 262K | | | | | | $0.25 | $1 |
|
|
60
|
+
| `wandb/Qwen/Qwen3.6-27B` | 262K | | | | | | $0.60 | $4 |
|
|
61
|
+
| `wandb/Qwen/Qwen3.6-35B-A3B` | 262K | | | | | | $0.25 | $1 |
|
|
62
|
+
| `wandb/zai-org/GLM-5.1` | 203K | | | | | | $1 | $4 |
|
|
63
|
+
| `wandb/zai-org/GLM-5.2` | 262K | | | | | | $1 | $4 |
|
|
57
64
|
|
|
58
65
|
## Advanced configuration
|
|
59
66
|
|
|
@@ -65,7 +72,7 @@ const agent = new Agent({
|
|
|
65
72
|
name: "custom-agent",
|
|
66
73
|
model: {
|
|
67
74
|
url: "https://api.inference.wandb.ai/v1",
|
|
68
|
-
id: "wandb/
|
|
75
|
+
id: "wandb/JetBrains/Mellum2-12B-A2.5B-Instruct",
|
|
69
76
|
apiKey: process.env.WANDB_API_KEY,
|
|
70
77
|
headers: {
|
|
71
78
|
"X-Custom-Header": "value"
|
|
@@ -83,8 +90,8 @@ const agent = new Agent({
|
|
|
83
90
|
model: ({ requestContext }) => {
|
|
84
91
|
const useAdvanced = requestContext.task === "complex";
|
|
85
92
|
return useAdvanced
|
|
86
|
-
? "wandb/zai-org/GLM-5.
|
|
87
|
-
: "wandb/
|
|
93
|
+
? "wandb/zai-org/GLM-5.2"
|
|
94
|
+
: "wandb/JetBrains/Mellum2-12B-A2.5B-Instruct";
|
|
88
95
|
}
|
|
89
96
|
});
|
|
90
97
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# xAI
|
|
4
4
|
|
|
5
|
-
Access
|
|
5
|
+
Access 9 xAI models through Mastra's model router. Authentication is handled automatically using the `XAI_API_KEY` environment variable.
|
|
6
6
|
|
|
7
7
|
Learn more in the [xAI documentation](https://docs.x.ai/docs/models).
|
|
8
8
|
|
|
@@ -38,6 +38,7 @@ for await (const chunk of stream) {
|
|
|
38
38
|
| `xai/grok-4.20-0309-reasoning` | 1.0M | | | | | | $1 | $3 |
|
|
39
39
|
| `xai/grok-4.20-multi-agent-0309` | 1.0M | | | | | | $1 | $3 |
|
|
40
40
|
| `xai/grok-4.3` | 1.0M | | | | | | $1 | $3 |
|
|
41
|
+
| `xai/grok-4.5` | 500K | | | | | | $2 | $6 |
|
|
41
42
|
| `xai/grok-build-0.1` | 256K | | | | | | $1 | $2 |
|
|
42
43
|
| `xai/grok-imagine-image` | 8K | | | | | | — | — |
|
|
43
44
|
| `xai/grok-imagine-image-quality` | 8K | | | | | | — | — |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
|
|
2
|
+
|
|
3
|
+
# Zenifra
|
|
4
|
+
|
|
5
|
+
Access 1 Zenifra model through Mastra's model router. Authentication is handled automatically using the `ZENIFRA_AI_KEY` environment variable.
|
|
6
|
+
|
|
7
|
+
Learn more in the [Zenifra documentation](https://docs.zenifra.com).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ZENIFRA_AI_KEY=your-api-key
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Agent } from "@mastra/core/agent";
|
|
15
|
+
|
|
16
|
+
const agent = new Agent({
|
|
17
|
+
id: "my-agent",
|
|
18
|
+
name: "My Agent",
|
|
19
|
+
instructions: "You are a helpful assistant",
|
|
20
|
+
model: "zenifra/alibaba/qwen3.6-35b-a3b"
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Generate a response
|
|
24
|
+
const response = await agent.generate("Hello!");
|
|
25
|
+
|
|
26
|
+
// Stream a response
|
|
27
|
+
const stream = await agent.stream("Tell me a story");
|
|
28
|
+
for await (const chunk of stream) {
|
|
29
|
+
console.log(chunk);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> **Info:** Mastra uses the OpenAI-compatible `/chat/completions` endpoint. Some provider-specific features may not be available. Check the [Zenifra documentation](https://docs.zenifra.com) for details.
|
|
34
|
+
|
|
35
|
+
## Models
|
|
36
|
+
|
|
37
|
+
| Model | Context | Tools | Reasoning | Image | Audio | Video | Input $/1M | Output $/1M |
|
|
38
|
+
| --------------------------------- | ------- | ----- | --------- | ----- | ----- | ----- | ---------- | ----------- |
|
|
39
|
+
| `zenifra/alibaba/qwen3.6-35b-a3b` | 262K | | | | | | $0.19 | $0.48 |
|
|
40
|
+
|
|
41
|
+
## Advanced configuration
|
|
42
|
+
|
|
43
|
+
### Custom headers
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const agent = new Agent({
|
|
47
|
+
id: "custom-agent",
|
|
48
|
+
name: "custom-agent",
|
|
49
|
+
model: {
|
|
50
|
+
url: "https://ai.zenifra.com/v1",
|
|
51
|
+
id: "zenifra/alibaba/qwen3.6-35b-a3b",
|
|
52
|
+
apiKey: process.env.ZENIFRA_AI_KEY,
|
|
53
|
+
headers: {
|
|
54
|
+
"X-Custom-Header": "value"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Dynamic model selection
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const agent = new Agent({
|
|
64
|
+
id: "dynamic-agent",
|
|
65
|
+
name: "Dynamic Agent",
|
|
66
|
+
model: ({ requestContext }) => {
|
|
67
|
+
const useAdvanced = requestContext.task === "complex";
|
|
68
|
+
return useAdvanced
|
|
69
|
+
? "zenifra/alibaba/qwen3.6-35b-a3b"
|
|
70
|
+
: "zenifra/alibaba/qwen3.6-35b-a3b";
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
```
|
|
@@ -136,6 +136,7 @@ Direct access to individual AI model providers. Each provider offers unique mode
|
|
|
136
136
|
- [Z.AI](https://mastra.ai/models/providers/zai)
|
|
137
137
|
- [Z.AI Coding Plan](https://mastra.ai/models/providers/zai-coding-plan)
|
|
138
138
|
- [Zeldoc](https://mastra.ai/models/providers/zeldoc)
|
|
139
|
+
- [Zenifra](https://mastra.ai/models/providers/zenifra)
|
|
139
140
|
- [ZenMux](https://mastra.ai/models/providers/zenmux)
|
|
140
141
|
- [Zhipu AI](https://mastra.ai/models/providers/zhipuai)
|
|
141
142
|
- [Zhipu AI Coding Plan](https://mastra.ai/models/providers/zhipuai-coding-plan)
|
package/.docs/reference/index.md
CHANGED
|
@@ -180,12 +180,14 @@ The Reference section provides documentation of Mastra's API, including paramete
|
|
|
180
180
|
- [Memory Class](https://mastra.ai/reference/memory/memory-class)
|
|
181
181
|
- [Observational Memory](https://mastra.ai/reference/memory/observational-memory)
|
|
182
182
|
- [SerializedMemoryConfig](https://mastra.ai/reference/memory/serialized-memory-config)
|
|
183
|
+
- [summarizeConversation()](https://mastra.ai/reference/memory/summarizeConversation)
|
|
183
184
|
- [.cloneThread()](https://mastra.ai/reference/memory/cloneThread)
|
|
184
185
|
- [.createThread()](https://mastra.ai/reference/memory/createThread)
|
|
185
186
|
- [.deleteMessages()](https://mastra.ai/reference/memory/deleteMessages)
|
|
186
187
|
- [.getThreadById()](https://mastra.ai/reference/memory/getThreadById)
|
|
187
188
|
- [.listThreads()](https://mastra.ai/reference/memory/listThreads)
|
|
188
189
|
- [.recall()](https://mastra.ai/reference/memory/recall)
|
|
190
|
+
- [.summarizeThread()](https://mastra.ai/reference/memory/summarizeThread)
|
|
189
191
|
- [Feedback](https://mastra.ai/reference/observability/feedback)
|
|
190
192
|
- [PinoLogger](https://mastra.ai/reference/logging/pino-logger)
|
|
191
193
|
- [Automatic Metrics](https://mastra.ai/reference/observability/metrics/automatic-metrics)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
|
|
2
|
+
|
|
3
|
+
# summarizeConversation()
|
|
4
|
+
|
|
5
|
+
The standalone `summarizeConversation()` function summarizes a conversation in one shot. It distills the messages you pass in with the same Observer plumbing that powers [Observational Memory](https://mastra.ai/reference/memory/observational-memory) — without Observational Memory attached to an agent, and without reading from or writing to storage.
|
|
6
|
+
|
|
7
|
+
Nothing is written back to memory. The summary and extracted values are returned to you (and to each extractor's `onExtracted` hook), so you decide where they go — for example your own database.
|
|
8
|
+
|
|
9
|
+
Use this when you already have the messages in hand and want explicit control over what gets summarized. To summarize a stored thread by ID instead, use [`Memory.summarizeThread()`](https://mastra.ai/reference/memory/summarizeThread), which loads the messages for you.
|
|
10
|
+
|
|
11
|
+
## Usage example
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { summarizeConversation } from '@mastra/memory'
|
|
15
|
+
|
|
16
|
+
const result = await summarizeConversation({
|
|
17
|
+
model: 'openai/gpt-5-mini',
|
|
18
|
+
messages,
|
|
19
|
+
instructions: 'Summarize this voicemail call for the business owner.',
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Parameters
|
|
24
|
+
|
|
25
|
+
**model** (`string | LanguageModel | DynamicModel`): Model that runs the summarization, such as a router string like '\_\_GATEWAY\_OPENAI\_MODEL\_MINI\_\_'.
|
|
26
|
+
|
|
27
|
+
**messages** (`MastraDBMessage[]`): The conversation to summarize. When empty, the function returns an empty result without calling the model.
|
|
28
|
+
|
|
29
|
+
**instructions** (`string`): Extra guidance appended to the summarizer's system prompt, such as what to focus on or who the summary is for.
|
|
30
|
+
|
|
31
|
+
**extract** (`Extractor[]`): Extractors to run over the conversation. Extractors with a Zod schema run as a follow-up structured output call; schema-less extractors are extracted inline. Each extractor's onExtracted hook fires with the extracted value.
|
|
32
|
+
|
|
33
|
+
**threadId** (`string`): Thread the conversation belongs to. Defaults to the first threadId found on messages. Passed to extractor contexts.
|
|
34
|
+
|
|
35
|
+
**resourceId** (`string`): Resource (user) the conversation belongs to. Defaults to the first resourceId found on messages. Passed to extractor contexts.
|
|
36
|
+
|
|
37
|
+
**memory** (`Memory`): Memory instance forwarded to extractor contexts. Set automatically when called through Memory.summarizeThread().
|
|
38
|
+
|
|
39
|
+
**mastra** (`Mastra`): Mastra instance used to resolve custom gateway models. Set automatically when called through Memory.summarizeThread().
|
|
40
|
+
|
|
41
|
+
**requestContext** (`RequestContext`): Request context forwarded to the summarizer model and extractor hooks.
|
|
42
|
+
|
|
43
|
+
**abortSignal** (`AbortSignal`): Signal to cancel the summarization call.
|
|
44
|
+
|
|
45
|
+
## Returns
|
|
46
|
+
|
|
47
|
+
**summary** (`string`): The distilled observations produced from the conversation, in dense bullet form.
|
|
48
|
+
|
|
49
|
+
**extracted** (`Record<string, unknown>`): Values produced by extract extractors, keyed by extractor slug.
|
|
50
|
+
|
|
51
|
+
**extractionFailures** (`{ slug: string; error: string }[]`): Extractors that failed to produce a valid value, with the reason. Only present when at least one extractor failed.
|
|
52
|
+
|
|
53
|
+
**usage** (`{ inputTokens?: number; outputTokens?: number; totalTokens?: number }`): Token usage of the summarization call.
|
|
54
|
+
|
|
55
|
+
## Extended usage example
|
|
56
|
+
|
|
57
|
+
The following example summarizes an in-flight voice session from messages the application already holds, and extracts a structured record:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { summarizeConversation, Extractor } from '@mastra/memory'
|
|
61
|
+
import type { MastraDBMessage } from '@mastra/core/agent'
|
|
62
|
+
import { z } from 'zod'
|
|
63
|
+
import { sessionRecords } from './db'
|
|
64
|
+
|
|
65
|
+
const sessionSummary = new Extractor({
|
|
66
|
+
name: 'session-summary',
|
|
67
|
+
instructions: 'Return a concise summary of the session.',
|
|
68
|
+
schema: z.object({
|
|
69
|
+
summary: z.string(),
|
|
70
|
+
sentiment: z.enum(['positive', 'neutral', 'negative']),
|
|
71
|
+
followUps: z.array(z.string()),
|
|
72
|
+
}),
|
|
73
|
+
metadataKeyPath: false, // don't persist into memory metadata — the hook owns storage
|
|
74
|
+
onExtracted: async ({ current, threadId }) => {
|
|
75
|
+
await sessionRecords.upsert({ sessionId: threadId, record: current })
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
export async function onSessionEnd(messages: MastraDBMessage[]) {
|
|
80
|
+
const { summary, extracted, extractionFailures } = await summarizeConversation({
|
|
81
|
+
model: 'openai/gpt-5-mini',
|
|
82
|
+
messages,
|
|
83
|
+
instructions: 'Summarize this support session for the account manager.',
|
|
84
|
+
extract: [sessionSummary],
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (extractionFailures?.length) {
|
|
88
|
+
console.warn('Some extractors failed', extractionFailures)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { summary, extracted }
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Related
|
|
96
|
+
|
|
97
|
+
- [.summarizeThread()](https://mastra.ai/reference/memory/summarizeThread)
|
|
98
|
+
- [Memory Class Reference](https://mastra.ai/reference/memory/memory-class)
|
|
99
|
+
- [Observational Memory](https://mastra.ai/reference/memory/observational-memory)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
|
|
2
|
+
|
|
3
|
+
# Memory.summarizeThread()
|
|
4
|
+
|
|
5
|
+
The `.summarizeThread()` method summarizes a thread's conversation in one shot. It loads the thread's messages from storage and distills them with the same Observer plumbing that powers [Observational Memory](https://mastra.ai/reference/memory/observational-memory) — as a standalone call, without Observational Memory attached to an agent.
|
|
6
|
+
|
|
7
|
+
Messages load page-by-page starting from the newest, bounded by `lastMessages` and `maxInputTokens`, so summarizing a long thread doesn't read its entire history from storage.
|
|
8
|
+
|
|
9
|
+
Nothing is written back to memory. The summary and extracted values are returned to you (and to each extractor's `onExtracted` hook), so you decide where they go — for example your own database.
|
|
10
|
+
|
|
11
|
+
Use this when a session ends and you want a summary or structured extraction of the whole conversation, such as a voice call at hang-up. To summarize messages you already have in hand (without loading them from a thread), use the standalone [`summarizeConversation()`](https://mastra.ai/reference/memory/summarizeConversation) function instead — it takes the same options with `messages` in place of `threadId`.
|
|
12
|
+
|
|
13
|
+
## Usage example
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
const result = await memory.summarizeThread({
|
|
17
|
+
model: 'openai/gpt-5-mini',
|
|
18
|
+
threadId: 'thread-123',
|
|
19
|
+
instructions: 'Summarize this voicemail call for the business owner.',
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Parameters
|
|
24
|
+
|
|
25
|
+
**threadId** (`string`): The unique identifier of the thread to summarize.
|
|
26
|
+
|
|
27
|
+
**model** (`string | LanguageModel | DynamicModel`): Model that runs the summarization, such as a router string like '\_\_GATEWAY\_OPENAI\_MODEL\_MINI\_\_'.
|
|
28
|
+
|
|
29
|
+
**resourceId** (`string`): ID of the resource that owns the thread. If provided, validates thread ownership. Also passed to extractor contexts.
|
|
30
|
+
|
|
31
|
+
**lastMessages** (`number`): Only summarize the last N messages of the thread. By default the whole thread is loaded, bounded by maxInputTokens.
|
|
32
|
+
|
|
33
|
+
**maxInputTokens** (`number`): Stop loading older messages once the collected messages exceed this estimated token count. The newest message is always included. (Default: `1000000`)
|
|
34
|
+
|
|
35
|
+
**instructions** (`string`): Extra guidance appended to the summarizer's system prompt, such as what to focus on or who the summary is for.
|
|
36
|
+
|
|
37
|
+
**extract** (`Extractor[]`): Extractors to run over the conversation. Extractors with a Zod schema run as a follow-up structured output call; schema-less extractors are extracted inline. Each extractor's onExtracted hook fires with the extracted value.
|
|
38
|
+
|
|
39
|
+
**requestContext** (`RequestContext`): Request context forwarded to the summarizer model and extractor hooks.
|
|
40
|
+
|
|
41
|
+
**abortSignal** (`AbortSignal`): Signal to cancel the summarization call.
|
|
42
|
+
|
|
43
|
+
## Returns
|
|
44
|
+
|
|
45
|
+
**summary** (`string`): The distilled observations produced from the conversation, in dense bullet form.
|
|
46
|
+
|
|
47
|
+
**extracted** (`Record<string, unknown>`): Values produced by extract extractors, keyed by extractor slug.
|
|
48
|
+
|
|
49
|
+
**extractionFailures** (`{ slug: string; error: string }[]`): Extractors that failed to produce a valid value, with the reason. Only present when at least one extractor failed.
|
|
50
|
+
|
|
51
|
+
**usage** (`{ inputTokens?: number; outputTokens?: number; totalTokens?: number }`): Token usage of the summarization call.
|
|
52
|
+
|
|
53
|
+
## Extended usage example
|
|
54
|
+
|
|
55
|
+
The following example summarizes a finished voice call and stores a structured record in the application's own database:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { Extractor } from '@mastra/memory'
|
|
59
|
+
import { z } from 'zod'
|
|
60
|
+
import { memory } from './mastra/memory'
|
|
61
|
+
import { callRecords } from './db'
|
|
62
|
+
|
|
63
|
+
const callSummary = new Extractor({
|
|
64
|
+
name: 'call-summary',
|
|
65
|
+
instructions: 'Return a concise summary of the call.',
|
|
66
|
+
schema: z.object({
|
|
67
|
+
summary: z.string(),
|
|
68
|
+
sentiment: z.enum(['positive', 'neutral', 'negative']),
|
|
69
|
+
requestedServices: z.array(z.string()),
|
|
70
|
+
}),
|
|
71
|
+
metadataKeyPath: false, // don't persist into memory metadata — the hook owns storage
|
|
72
|
+
onExtracted: async ({ current, threadId, resourceId }) => {
|
|
73
|
+
await callRecords.upsert({ callId: threadId, callerId: resourceId, record: current })
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export async function onCallEnd(threadId: string, resourceId: string) {
|
|
78
|
+
await memory.summarizeThread({
|
|
79
|
+
model: 'openai/gpt-5-mini',
|
|
80
|
+
threadId,
|
|
81
|
+
resourceId,
|
|
82
|
+
instructions: 'Summarize this voicemail call for the business owner.',
|
|
83
|
+
extract: [callSummary],
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Related
|
|
89
|
+
|
|
90
|
+
- [summarizeConversation()](https://mastra.ai/reference/memory/summarizeConversation)
|
|
91
|
+
- [Memory Class Reference](https://mastra.ai/reference/memory/memory-class)
|
|
92
|
+
- [Observational Memory](https://mastra.ai/reference/memory/observational-memory)
|
|
93
|
+
- [.recall()](https://mastra.ai/reference/memory/recall)
|
|
@@ -6,10 +6,11 @@ The `@mastra/livekit` package connects Mastra agents to the LiveKit Agents frame
|
|
|
6
6
|
|
|
7
7
|
See [Using LiveKit with Mastra](https://mastra.ai/docs/voice/livekit) for setup and concepts.
|
|
8
8
|
|
|
9
|
-
The package has
|
|
9
|
+
The package has three entry points:
|
|
10
10
|
|
|
11
|
-
- `@mastra/livekit`: server-side APIs — [`liveKitConnectionRoute()`](#livekitconnectionroute), [`dispatchVoiceSession()`](#dispatchvoicesession), [`pipeAgentReplyToWriter()`](#pipeagentreplytowriter),
|
|
12
|
-
- `@mastra/livekit/worker`: the worker runtime — [`createLiveKitWorker()`](#createlivekitworker), [`runLiveKitWorker()`](#runlivekitworker),
|
|
11
|
+
- `@mastra/livekit`: server-side APIs — [`liveKitConnectionRoute()`](#livekitconnectionroute), [`dispatchVoiceSession()`](#dispatchvoicesession), [`pipeAgentReplyToWriter()`](#pipeagentreplytowriter), [`serializeSessionMetadata()`](#livekitsessionmetadata), and [`createEndCallTool()`](#createendcalltool). Import these from Mastra server code. This entry never loads the LiveKit agents runtime.
|
|
12
|
+
- `@mastra/livekit/worker`: the worker runtime — [`createLiveKitWorker()`](#createlivekitworker), [`runLiveKitWorker()`](#runlivekitworker), [`chatContextToMessages()`](#chatcontexttomessages), and the session helpers [`speakGreeting()`](#speakgreeting), [`waitForAgentDoneSpeaking()`](#waitforagentdonespeaking), and [`runEndCall()`](#runendcall). Import it only from the worker entry file.
|
|
13
|
+
- `@mastra/livekit/plugin`: the LLM-component plugin — [`MastraLLM`](#mastrallm) and [`createRemoteAgentReplyGenerator()`](#createremoteagentreplygenerator). Import it in workers that build their own `voice.AgentSession`. `createRemoteAgentReplyGenerator()` is also exported from `@mastra/livekit/worker` because it plugs into `createLiveKitWorker()`'s `generate` option; `MastraLLM` is plugin-only.
|
|
13
14
|
|
|
14
15
|
## `createLiveKitWorker()`
|
|
15
16
|
|
|
@@ -49,9 +50,9 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
49
50
|
|
|
50
51
|
**generate** (`VoiceReplyGenerator`): Lowest-level escape hatch: supply any reply generator directly (a custom workflow, remote bridge, and so on).
|
|
51
52
|
|
|
52
|
-
**stt** (`STT | string`): Speech-to-text: a LiveKit plugin instance or an inference model string such as 'deepgram/nova-3'.
|
|
53
|
+
**stt** (`STT | string`): Speech-to-text: a LiveKit plugin instance or an inference model string such as 'deepgram/nova-3'. For per-call selection, set the configuration.stt resolver — it takes precedence, with this option as the fallback.
|
|
53
54
|
|
|
54
|
-
**tts** (`TTS | string`): Text-to-speech: a LiveKit plugin instance or an inference model string such as 'cartesia/sonic-3'.
|
|
55
|
+
**tts** (`TTS | string`): Text-to-speech: a LiveKit plugin instance or an inference model string such as 'cartesia/sonic-3'. For per-call selection, set the configuration.tts resolver — it takes precedence, with this option as the fallback.
|
|
55
56
|
|
|
56
57
|
**vad** (`VAD | 'silero' | false`): Voice activity detection. 'silero' loads the Silero VAD from @livekit/agents-plugin-silero during prewarm. Pass an instance to bring your own, or false to disable. (Default: `'silero'`)
|
|
57
58
|
|
|
@@ -65,9 +66,23 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
65
66
|
|
|
66
67
|
**toolFeedback** (`(toolCall) => string | undefined`): Called when the Mastra agent starts a tool call mid-reply. Return a short phrase to speak while the tool runs.
|
|
67
68
|
|
|
68
|
-
**
|
|
69
|
+
**onTurnComplete** (`(ctx: VoiceTurnCompleteContext) => void | Promise<void>`): Called once per turn after the reply finished streaming to text-to-speech. Runs off the audio path and is not awaited. The context carries the produced reply (text, toolCalls, interrupted, usage) and the resolved memory mapping.
|
|
69
70
|
|
|
70
|
-
**
|
|
71
|
+
**configuration** (`LiveKitWorkerConfiguration`): Grouped conversation and compliance configuration: the opening greeting and AI disclosure, consent requirements, agent-initiated hang-up, and per-call STT/TTS selection.
|
|
72
|
+
|
|
73
|
+
**configuration.greeting** (`GreetingConfiguration`): The opening greeting and AI disclosure: text (a fixed string or a per-call resolver for per-tenant greetings), allowInterruptions, awaitPlayout, persist, and periodic re-disclosure via repeatEvery and repeatText.
|
|
74
|
+
|
|
75
|
+
**configuration.consentPolicy** (`ConsentConfiguration`): The call's consent policy, as named requirements (starting with summaryStorage). Declarative only — the worker blocks nothing by itself. Capture grants at runtime with createConsentTool and enforce them in your own code; the declared policy surfaces on onCallEnd for cross-checking.
|
|
76
|
+
|
|
77
|
+
**configuration.endCall** (`EndCallConfiguration`): Agent-initiated hang-up: the worker watches each turn for the end-call tool (pair with createEndCallTool), waits for the agent's closing words to play out, then disconnects — running onCallEnd on the way out.
|
|
78
|
+
|
|
79
|
+
**configuration.stt** (`(context: VoiceCallContext) => STT | string | undefined`): Per-call speech-to-text: a resolver invoked once per call (post-connect) with { metadata, requestContext, roomName, ctx }, returning anything the top-level stt option accepts. Return undefined to fall back to the top-level stt. Cache plugin instances across calls — the resolver runs during call setup.
|
|
80
|
+
|
|
81
|
+
**configuration.tts** (`(context: VoiceCallContext) => TTS | string | undefined`): Per-call text-to-speech: a resolver invoked once per call (post-connect) with { metadata, requestContext, roomName, ctx }, returning anything the top-level tts option accepts — one voice or language per tenant. Return undefined to fall back to the top-level tts. Cache plugin instances across calls.
|
|
82
|
+
|
|
83
|
+
**greeting** (`string`): Static greeting spoken when the session starts. Deprecated: prefer configuration.greeting.text.
|
|
84
|
+
|
|
85
|
+
**persistGreeting** (`boolean`): Save the spoken greeting to the memory thread as an assistant message, making the saved thread a faithful call transcript. Only applies when a greeting is set and memory is enabled. Deprecated: prefer configuration.greeting.persist. (Default: `true`)
|
|
71
86
|
|
|
72
87
|
**observability** (`boolean`): Trace each call when the Mastra instance has observability configured. Opens a voice call span per session: every turn's agent run nests under it, LiveKit's STT, TTS, end-of-utterance, VAD, and LLM latency metrics become child spans, and the span closes with a per-model usage roll-up. Pass false to disable. (Default: `true`)
|
|
73
88
|
|
|
@@ -129,7 +144,256 @@ export default createLiveKitWorker({
|
|
|
129
144
|
})
|
|
130
145
|
```
|
|
131
146
|
|
|
132
|
-
Returns: `VoiceTurnMessage[]`, where each entry is `{ role: 'system' | 'user' | 'assistant'; content: string }`.
|
|
147
|
+
Returns: `VoiceTurnMessage[]`, where each entry is `{ role: 'system' | 'user' | 'assistant'; content: string; id?: string }`.
|
|
148
|
+
|
|
149
|
+
## `MastraLLM`
|
|
150
|
+
|
|
151
|
+
A standard LiveKit LLM plugin (`llm.LLM`) backed by a Mastra agent. Use it when you build the `voice.AgentSession` yourself and want Mastra in the `llm` slot; [`createLiveKitWorker()`](#createlivekitworker) is the managed alternative. See [Use Mastra as the LLM component](https://mastra.ai/docs/voice/livekit) for how to choose.
|
|
152
|
+
|
|
153
|
+
With `remote`, the plugin streams each turn from your Mastra server over HTTP using Server-Sent Events (SSE). The agent loop, tools, and memory run server-side, and interrupting the agent aborts the server-side generation.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { voice } from '@livekit/agents'
|
|
157
|
+
import { MastraLLM } from '@mastra/livekit/plugin'
|
|
158
|
+
|
|
159
|
+
const session = new voice.AgentSession({
|
|
160
|
+
llm: new MastraLLM({
|
|
161
|
+
remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'support' },
|
|
162
|
+
memory: { thread: callId, resource: userId },
|
|
163
|
+
}),
|
|
164
|
+
stt: 'deepgram/nova-3',
|
|
165
|
+
tts: 'cartesia/sonic-3',
|
|
166
|
+
// Required with `memory`: LiveKit enables preemptive generation by default.
|
|
167
|
+
turnHandling: { preemptiveGeneration: { enabled: false } },
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The plugin reports `provider` as `mastra` and `model` as the agent id, so LiveKit metrics and fallback adapters identify it like any other LLM.
|
|
172
|
+
|
|
173
|
+
### Constructor options
|
|
174
|
+
|
|
175
|
+
Provide exactly one reply source: `remote`, `agent`, or `generate`.
|
|
176
|
+
|
|
177
|
+
**remote** (`RemoteMastraAgentOptions`): Remote Mastra server reached over HTTP. Takes the same connection options as createRemoteAgentReplyGenerator(): baseUrl, agentId, apiPrefix, headers, fetch, timeoutMs, retries, body.
|
|
178
|
+
|
|
179
|
+
**agent** (`Agent`): In-process Mastra agent. Session ownership without a second deployment.
|
|
180
|
+
|
|
181
|
+
**generate** (`VoiceReplyGenerator`): Custom reply source. A generate source owns its own hooks; toolFeedback, onToolCall, and onTurnComplete below only apply to the remote and agent sources.
|
|
182
|
+
|
|
183
|
+
**memory** (`{ thread: string; resource?: string } | false`): Conversation persistence, resolved per call (for example from the SIP caller identity). When set, only messages new since the agent last spoke are sent each turn and Mastra Memory supplies history. When omitted, the full LiveKit chat context is sent every turn. (Default: `false`)
|
|
184
|
+
|
|
185
|
+
**requestContext** (`RequestContext | Record<string, unknown>`): Request context forwarded to generation (tenant, dialed number, and so on).
|
|
186
|
+
|
|
187
|
+
**toolFeedback** (`(toolCall: VoiceToolCall) => string | undefined`): Return a short phrase to speak while a server-side tool runs.
|
|
188
|
+
|
|
189
|
+
**onToolCall** (`(toolCall: VoiceToolCall) => void`): Called as each tool call starts, mid-stream. Pair with runEndCall() to implement your own agent-initiated hang-up flow.
|
|
190
|
+
|
|
191
|
+
**onTurnComplete** (`(ctx: VoiceTurnCompleteContext) => void | Promise<void>`): Called once per turn after the reply finished streaming, off the audio path and not awaited. The context carries the produced reply: text, toolCalls, interrupted, and usage.
|
|
192
|
+
|
|
193
|
+
> **Warning:** Don't combine `memory` with the session's `preemptiveGeneration` option, which LiveKit enables by default in sessions you build yourself. A speculative turn that completes before LiveKit discards it persists a user message and a never-spoken reply to the thread. Set `turnHandling: { preemptiveGeneration: { enabled: false } }` on the session. Stateless mode (no `memory`) works with preemptive generation.
|
|
194
|
+
|
|
195
|
+
### Tools run on the Mastra agent
|
|
196
|
+
|
|
197
|
+
Tools are defined and executed server-side on the Mastra agent. The plugin never forwards LiveKit tool definitions: if the session passes a non-empty `toolCtx`, it logs a one-time warning naming the ignored tools. Every tool must complete server-side — a tool that requires approval or client-side execution fails the turn with a descriptive error instead of hanging the call.
|
|
198
|
+
|
|
199
|
+
Tool activity reaches the worker through `toolFeedback`, `onToolCall`, and `onTurnComplete`.
|
|
200
|
+
|
|
201
|
+
### Instructions
|
|
202
|
+
|
|
203
|
+
LiveKit injects your `voice.Agent`'s `instructions` into the chat context of every request. The plugin drops them: the server-side Mastra agent's own instructions are authoritative. To change the prompt, change the Mastra agent.
|
|
204
|
+
|
|
205
|
+
### Interrupted turns
|
|
206
|
+
|
|
207
|
+
When the user interrupts a reply:
|
|
208
|
+
|
|
209
|
+
1. The plugin cancels the stream. The server aborts generation and persists nothing from that turn.
|
|
210
|
+
2. LiveKit records the part the user actually heard in its chat context, flagged as interrupted.
|
|
211
|
+
3. On the next turn, the plugin re-sends that heard-only fragment, ordered before the new user message, so the memory thread backfills to match the call. Messages carry LiveKit's message ids and the server deduplicates by id, so retries and re-sends stay idempotent.
|
|
212
|
+
|
|
213
|
+
A user who hangs up immediately after interrupting leaves that final fragment unrecorded. When the transcript must capture it, reconcile immediately from the session event; the shared message id means the next turn's re-send upserts instead of duplicating:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { voice } from '@livekit/agents'
|
|
217
|
+
import { MastraClient } from '@mastra/client-js'
|
|
218
|
+
|
|
219
|
+
const client = new MastraClient({ baseUrl: process.env.MASTRA_URL! })
|
|
220
|
+
|
|
221
|
+
session.on(voice.AgentSessionEventTypes.ConversationItemAdded, ({ item }) => {
|
|
222
|
+
if (item.type !== 'message' || item.role !== 'assistant' || !item.interrupted) return
|
|
223
|
+
void client.saveMessageToMemory({
|
|
224
|
+
agentId: 'support',
|
|
225
|
+
messages: [
|
|
226
|
+
{
|
|
227
|
+
id: item.id,
|
|
228
|
+
threadId: callId,
|
|
229
|
+
resourceId: userId,
|
|
230
|
+
role: 'assistant',
|
|
231
|
+
content: item.textContent ?? '',
|
|
232
|
+
type: 'text',
|
|
233
|
+
createdAt: new Date(),
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Usage metrics
|
|
241
|
+
|
|
242
|
+
When the server reports token usage for a turn, the plugin feeds it to LiveKit, so the session's `metrics_collected` events carry time-to-first-token, duration, and token counts like any LLM plugin. The same usage object (`promptTokens`, `completionTokens`, `promptCachedTokens`, `totalTokens`) arrives on `onTurnComplete` as `result.usage`.
|
|
243
|
+
|
|
244
|
+
### Errors and timeouts
|
|
245
|
+
|
|
246
|
+
The transport throws LiveKit's `APIError` types (`APIStatusError`, `APIConnectionError`, `APITimeoutError`), so the session's retry policy (`connOptions.maxRetry`) and `FallbackAdapter` failover work unchanged. A turn is never retried after its first token — a voice reply is better failed fast than replayed half-heard.
|
|
247
|
+
|
|
248
|
+
A connect and first-token watchdog uses the session's `connOptions.timeoutMs` (10 seconds by default), so a server that accepts the connection but never streams can't cause indefinite dead air.
|
|
249
|
+
|
|
250
|
+
If the Mastra server goes down mid-call, each reply attempt fails with a typed error after its retries, and LiveKit closes the session after several consecutive failed replies. Restore the server before that budget runs out and the call recovers on the next turn.
|
|
251
|
+
|
|
252
|
+
### Message content
|
|
253
|
+
|
|
254
|
+
Message extraction is text-only: image content is dropped, and audio content is included only through its transcript. Voice pipelines aren't affected, but items you inject into the chat context yourself must carry text.
|
|
255
|
+
|
|
256
|
+
## `createRemoteAgentReplyGenerator()`
|
|
257
|
+
|
|
258
|
+
Builds a reply generator that runs the agent loop on a **remote** Mastra server over HTTP/SSE. `MastraLLM`'s `remote` mode uses it internally. Use it directly through `createLiveKitWorker`'s `generate` option to run the batteries-included worker against a remote server:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { createLiveKitWorker, createRemoteAgentReplyGenerator } from '@mastra/livekit/worker'
|
|
262
|
+
import { mastra } from './index'
|
|
263
|
+
|
|
264
|
+
export default createLiveKitWorker({
|
|
265
|
+
mastra, // local instance for logger and worker config; replies come from the remote server
|
|
266
|
+
generate: createRemoteAgentReplyGenerator({
|
|
267
|
+
baseUrl: process.env.MASTRA_URL!,
|
|
268
|
+
agentId: 'support',
|
|
269
|
+
}),
|
|
270
|
+
memory: ({ metadata, roomName }) => ({ thread: metadata.threadId ?? roomName }),
|
|
271
|
+
stt: 'deepgram/nova-3',
|
|
272
|
+
tts: 'cartesia/sonic-3',
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
On the `generate` path the worker-level `toolFeedback` and `onTurnComplete` options don't apply, and the worker's end-call detection doesn't fire; pass the hooks to the generator instead.
|
|
277
|
+
|
|
278
|
+
Cancelling a turn (barge-in) tears down the HTTP request, which aborts generation on the server. Errors are thrown as LiveKit `APIError` types; `retries` applies to the initial connection only — a turn is never retried after its first chunk.
|
|
279
|
+
|
|
280
|
+
Returns: `VoiceReplyGenerator`.
|
|
281
|
+
|
|
282
|
+
### Options
|
|
283
|
+
|
|
284
|
+
**baseUrl** (`string`): Base URL of the remote Mastra server, for example https\://my-app.example.com.
|
|
285
|
+
|
|
286
|
+
**agentId** (`string`): The agent's registered key or id on the remote Mastra instance.
|
|
287
|
+
|
|
288
|
+
**apiPrefix** (`string`): Path prefix for the Mastra API. (Default: `'/api'`)
|
|
289
|
+
|
|
290
|
+
**headers** (`Record<string, string> | () => Record<string, string> | Promise<Record<string, string>>`): Static headers, or a resolver invoked per turn — for example to mint a fresh authorization token.
|
|
291
|
+
|
|
292
|
+
**fetch** (`typeof fetch`): Injectable fetch implementation for tests or proxies. (Default: `globalThis.fetch`)
|
|
293
|
+
|
|
294
|
+
**timeoutMs** (`number`): Connect and first-token timeout in milliseconds. When used through MastraLLM, defaults to the session's connOptions.timeoutMs instead. (Default: `10000`)
|
|
295
|
+
|
|
296
|
+
**retries** (`number`): Initial-connection retry attempts, before the first chunk only. When used through MastraLLM, the LiveKit session owns retries and this is forced to 0. (Default: `2`)
|
|
297
|
+
|
|
298
|
+
**body** (`Record<string, unknown>`): Extra fields merged into each stream request body.
|
|
299
|
+
|
|
300
|
+
**toolFeedback** (`(toolCall: VoiceToolCall) => string | undefined`): Return a short phrase to speak while a server-side tool runs.
|
|
301
|
+
|
|
302
|
+
**onToolCall** (`(toolCall: VoiceToolCall) => void`): Called as each tool call starts, mid-stream.
|
|
303
|
+
|
|
304
|
+
**onTurnComplete** (`(ctx: VoiceTurnCompleteContext) => void | Promise<void>`): Called once per turn after the reply finished streaming, off the audio path.
|
|
305
|
+
|
|
306
|
+
## `speakGreeting()`
|
|
307
|
+
|
|
308
|
+
Speaks an opening greeting on a session you own, honoring interruption and playout options. Returns the LiveKit `SpeechHandle`, or `undefined` when there's no greeting text. `createLiveKitWorker()` uses it internally for its `greeting` configuration.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { speakGreeting } from '@mastra/livekit/worker'
|
|
312
|
+
|
|
313
|
+
await speakGreeting(session, {
|
|
314
|
+
text: "You've reached support. You're speaking with an AI assistant.",
|
|
315
|
+
allowInterruptions: false,
|
|
316
|
+
awaitPlayout: true,
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Parameters
|
|
321
|
+
|
|
322
|
+
**session** (`voice.AgentSession`): The session to speak on.
|
|
323
|
+
|
|
324
|
+
**greeting** (`{ text?: string; allowInterruptions?: boolean; awaitPlayout?: boolean }`): The greeting text and playout options. When awaitPlayout is true, the returned promise resolves after the greeting finished playing (or was interrupted).
|
|
325
|
+
|
|
326
|
+
## `waitForAgentDoneSpeaking()`
|
|
327
|
+
|
|
328
|
+
Resolves once the agent is no longer producing or playing a reply — its state has left `thinking` and `speaking`. Resolves immediately when the agent is already idle, and always resolves within `maxWaitMs` (30 seconds by default) as a safety cap. Use it before tearing a session down so closing words play out instead of being cut off.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { waitForAgentDoneSpeaking } from '@mastra/livekit/worker'
|
|
332
|
+
|
|
333
|
+
await waitForAgentDoneSpeaking(session)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## `runEndCall()`
|
|
337
|
+
|
|
338
|
+
Ends the call after the agent asked to: waits for the agent's closing words to finish, speaks an optional final `message` non-interruptibly, then deletes the room (hanging up the caller, SIP included) and shuts the job down, which runs registered shutdown callbacks.
|
|
339
|
+
|
|
340
|
+
Pair it with [`MastraLLM`](#mastrallm)'s `onToolCall` and an [end-call tool](#createendcalltool) on the server-side agent to rebuild agent-initiated hang-up on a session you own:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import { MastraLLM } from '@mastra/livekit/plugin'
|
|
344
|
+
import { DEFAULT_END_CALL_TOOL, runEndCall } from '@mastra/livekit/worker'
|
|
345
|
+
|
|
346
|
+
let ending = false
|
|
347
|
+
|
|
348
|
+
const llm = new MastraLLM({
|
|
349
|
+
remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'support' },
|
|
350
|
+
onToolCall: ({ toolName }) => {
|
|
351
|
+
if (toolName !== DEFAULT_END_CALL_TOOL || ending) return
|
|
352
|
+
ending = true
|
|
353
|
+
void runEndCall(session, ctx, {}, console)
|
|
354
|
+
},
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The exported constants `DEFAULT_END_CALL_TOOL` (`'endCall'`), `DEFAULT_END_CALL_REASON`, and `DEFAULT_END_CALL_MAX_WAIT_MS` (30000) hold the defaults.
|
|
359
|
+
|
|
360
|
+
### Parameters
|
|
361
|
+
|
|
362
|
+
**session** (`voice.AgentSession`): The session whose agent is finishing its closing words.
|
|
363
|
+
|
|
364
|
+
**ctx** (`JobContext`): The LiveKit job context used to delete the room and shut down.
|
|
365
|
+
|
|
366
|
+
**config** (`{ message?: string; reason?: string; maxWaitMs?: number; drainMs?: number }`): Optional final message spoken before hang-up, the shutdown reason to record, the safety cap on waiting for closing words, and the post-playout drain (default 800ms) that lets audio buffered at the caller finish playing before the room is deleted — LiveKit's playout accounting is worker-local, so hanging up the instant it clears clips the goodbye.
|
|
367
|
+
|
|
368
|
+
**logger** (`{ warn: (message: string, ...args: unknown[]) => void }`): Receives warnings when teardown steps fail. Pass your logger or console.
|
|
369
|
+
|
|
370
|
+
## `createEndCallTool()`
|
|
371
|
+
|
|
372
|
+
Builds the Mastra tool an agent calls to end the call itself: say goodbye, then hang up. The tool only signals intent (and runs optional bookkeeping) — the worker owns the actual hang-up. It lives on the server-safe root entry, so add it to agents defined in server code.
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { Agent } from '@mastra/core/agent'
|
|
376
|
+
import { createEndCallTool } from '@mastra/livekit'
|
|
377
|
+
|
|
378
|
+
const supportAgent = new Agent({
|
|
379
|
+
id: 'support',
|
|
380
|
+
name: 'Support',
|
|
381
|
+
instructions:
|
|
382
|
+
'Help the caller. When everything is wrapped up, say goodbye and call endCall as your final action.',
|
|
383
|
+
model: 'openai/gpt-5-mini',
|
|
384
|
+
tools: { endCall: createEndCallTool() },
|
|
385
|
+
})
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
With `createLiveKitWorker()`, set `configuration: { endCall: {} }` and the worker watches for the tool and hangs up. On a session you own, rebuild the hang-up with [`runEndCall()`](#runendcall).
|
|
389
|
+
|
|
390
|
+
### Options
|
|
391
|
+
|
|
392
|
+
**id** (`string`): Tool id the agent calls to end the call. Must match the name the worker watches for (the worker's configuration.endCall.tool, or your own onToolCall check). (Default: `'endCall'`)
|
|
393
|
+
|
|
394
|
+
**description** (`string`): Override the description the model sees when deciding to call the tool.
|
|
395
|
+
|
|
396
|
+
**onEndCall** (`(request: { reason?: string; resourceId?: string; threadId?: string }) => void | Promise<void>`): Bookkeeping hook called when the agent invokes the tool — record the reason or mark the call resolved. Runs inside the turn; keep it quick. It does not hang up the call.
|
|
133
397
|
|
|
134
398
|
## `liveKitConnectionRoute()`
|
|
135
399
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp-docs-server",
|
|
3
|
-
"version": "1.2.7-alpha.
|
|
3
|
+
"version": "1.2.7-alpha.4",
|
|
4
4
|
"description": "MCP server for accessing Mastra.ai documentation, changelogs, and news.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"tsx": "^4.22.4",
|
|
46
46
|
"typescript": "^6.0.3",
|
|
47
47
|
"vitest": "4.1.9",
|
|
48
|
+
"@internal/lint": "0.0.113",
|
|
48
49
|
"@internal/types-builder": "0.0.88",
|
|
49
|
-
"@mastra/core": "1.50.2-alpha.1"
|
|
50
|
-
"@internal/lint": "0.0.113"
|
|
50
|
+
"@mastra/core": "1.50.2-alpha.1"
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://mastra.ai",
|
|
53
53
|
"repository": {
|