LiveKit
Le package @mastra/livekit connecte les agents Mastra au framework LiveKit Agents. LiveKit exécute le pipeline audio (détection d’activité vocale, transcription de la parole, détection des tours de parole, synthèse vocale, interruption) et le package relie la génération des réponses à l’appel stream() d’un agent Mastra.
Consultez Voix en temps réel pour découvrir la configuration et les concepts.
Le package comporte trois points d’entrée :
@mastra/livekit: les API côté serveur,liveKitConnectionRoute(),dispatchVoiceSession(),pipeAgentReplyToWriter(),serializeSessionMetadata()etcreateEndCallTool(). Importez-les depuis le code serveur Mastra. Ce point d’entrée ne charge jamais le runtime des agents LiveKit.@mastra/livekit/worker: le runtime du worker,createLiveKitWorker(),runLiveKitWorker(),chatContextToMessages(), ainsi que les utilitaires de sessionspeakGreeting(),waitForAgentDoneSpeaking()etrunEndCall(). Importez-le uniquement depuis le fichier d’entrée du worker.@mastra/livekit/plugin: le plugin de composant LLM,MastraLLMetcreateRemoteAgentReplyGenerator(). Importez-le dans les workers qui construisent leur proprevoice.AgentSession.createRemoteAgentReplyGenerator()est également exporté depuis@mastra/livekit/worker, car il s’intègre àcreateLiveKitWorker()par l’intermédiaire de son optiongenerate.MastraLLMest disponible uniquement dans le plugin.
createLiveKitWorker()Lien direct vers createlivekitworker
Construit une définition d’agent LiveKit qui répond aux sessions vocales avec des agents Mastra. Utilisez-la comme export par défaut du fichier d’entrée de votre worker.
import { fileURLToPath } from 'node:url'
import { createLiveKitWorker, runLiveKitWorker } from '@mastra/livekit/worker'
import { mastra } from './index'
export default createLiveKitWorker({
mastra,
agent: 'support',
stt: 'deepgram/nova-3',
tts: 'cartesia/sonic-3',
turnDetection: 'multilingual',
})
if (process.argv[1] === fileURLToPath(import.meta.url)) {
runLiveKitWorker({ entry: import.meta.url, agentName: 'mastra-voice' })
}
OptionsLien direct vers Options
mastra:
agent?:
workflow?:
workflowInput?:
replyStep?:
resultText?:
generate?:
stt?:
tts?:
vad?:
turnDetection?:
turnHandling?:
sessionOptions?:
memory?:
toolFeedback?:
onTurnComplete?:
configuration?:
greeting?:
consentPolicy?:
endCall?:
stt?:
tts?:
greeting?:
persistGreeting?:
observability?:
voice call par session : l’exécution de l’agent à chaque tour y est imbriquée, les métriques LiveKit de latence STT, TTS, de fin d’énoncé, VAD et LLM deviennent des spans enfants, et le span se ferme avec un récapitulatif de l’utilisation par modèle. Transmettez false pour désactiver cette fonction.inputOptions?:
outputOptions?:
onSessionStart?:
runLiveKitWorker()Lien direct vers runlivekitworker
Démarre la CLI du worker LiveKit (sous-commandes dev, start et connect) pour un fichier d’entrée de worker. Appelez-la depuis le fichier qui exporte par défaut la définition du worker, avec une condition garantissant qu’elle ne s’exécute que lorsque le fichier est lancé directement (le worker crée un processus enfant par session, qui réimporte le même fichier). L’utilisation de cet utilitaire plutôt que de cli.runApp depuis @livekit/agents garantit que le runtime du worker et le pont partagent une même copie du SDK LiveKit.
OptionsLien direct vers Options
entry:
agentName?:
serverOptions?:
pipeAgentReplyToWriter()Lien direct vers pipeagentreplytowriter
Diffuse la réponse d’un agent Mastra dans le writer d’une étape du workflow sur le chemin de réponse du workflow. Cette fonction transmet les deltas de texte de l’agent, afin que la synthèse vocale commence avant que la réponse complète soit prête, ainsi que ses fragments d’appels d’outils, afin que toolFeedback se déclenche et que onTurnComplete reçoive la liste des outils. Ne rediriger que stream.textStream supprime silencieusement les appels d’outils. Transmettez le abortSignal de l’étape à agent.stream() afin qu’une interruption arrête rapidement la génération.
import { pipeAgentReplyToWriter } from '@mastra/livekit'
const generateResponse = createStep({
id: 'generateResponse',
// input and output schemas omitted
execute: async ({ inputData, mastra, writer, abortSignal }) => {
const stream = await mastra.getAgent('support').stream(inputData.turn, { abortSignal })
const reply = await pipeAgentReplyToWriter(stream, writer)
return { reply }
},
})
Renvoie : Promise<string>, le texte de réponse accumulé.
ParamètresLien direct vers Paramètres
agentStream:
writer:
chatContextToMessages()Lien direct vers chatcontexttomessages
Convertit un contexte de chat LiveKit en messages simples acceptés par agent.stream(), en excluant les instructions et les appels de fonctions. Utilisez cette fonction dans workflowInput pour transmettre la transcription complète à un workflow sans état.
import { createLiveKitWorker, chatContextToMessages } from '@mastra/livekit/worker'
export default createLiveKitWorker({
mastra,
workflow: 'phoneConversation',
workflowInput: ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }),
})
Renvoie : VoiceTurnMessage[], où chaque entrée est { role: 'system' | 'user' | 'assistant'; content: string; id?: string }.
MastraLLMLien direct vers mastrallm
Un plugin LLM LiveKit standard (llm.LLM) reposant sur un agent Mastra. Utilisez-le lorsque vous construisez vous-même la voice.AgentSession et souhaitez placer Mastra dans l’emplacement llm. createLiveKitWorker() constitue l’alternative gérée. Consultez Utiliser Mastra comme composant LLM pour choisir la solution appropriée.
Avec remote, le plugin diffuse chaque tour depuis votre serveur Mastra via HTTP à l’aide de Server-Sent Events (SSE). La boucle de l’agent, les outils et la mémoire s’exécutent côté serveur ; interrompre l’agent annule la génération côté serveur.
import { voice } from '@livekit/agents'
import { MastraLLM } from '@mastra/livekit/plugin'
const session = new voice.AgentSession({
llm: new MastraLLM({
remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'support' },
memory: { thread: callId, resource: userId },
}),
stt: 'deepgram/nova-3',
tts: 'cartesia/sonic-3',
// Required with `memory`: LiveKit enables preemptive generation by default.
turnHandling: { preemptiveGeneration: { enabled: false } },
})
Le plugin définit provider sur mastra et model sur l’identifiant de l’agent, afin que les métriques LiveKit et les adaptateurs de repli l’identifient comme n’importe quel autre LLM.
Options du constructeurLien direct vers Options du constructeur
Fournissez exactement une source de réponse : remote, agent ou generate.
remote?:
agent?:
generate?:
memory?:
requestContext?:
toolFeedback?:
onToolCall?:
onTurnComplete?:
N’associez pas memory à l’option preemptiveGeneration de la session, que LiveKit active par défaut dans les sessions que vous construisez vous-même. Un tour spéculatif qui se termine avant que LiveKit ne l’abandonne conserve dans le thread un message utilisateur et une réponse jamais prononcée. Définissez turnHandling: { preemptiveGeneration: { enabled: false } } sur la session. Le mode sans état (sans memory) fonctionne avec la génération anticipée.
Exécution des outils sur l’agent MastraLien direct vers Exécution des outils sur l’agent Mastra
Les outils sont définis et exécutés côté serveur sur l’agent Mastra. Le plugin ne transmet jamais les définitions d’outils LiveKit : si la session fournit un toolCtx non vide, il consigne une seule fois un avertissement indiquant les outils ignorés. Chaque outil doit terminer son exécution côté serveur : un outil qui nécessite une approbation ou une exécution côté client fait échouer le tour avec une erreur descriptive au lieu de bloquer l’appel.
L’activité des outils parvient au worker via toolFeedback, onToolCall et onTurnComplete.
InstructionsLien direct vers Instructions
LiveKit injecte, depuis votre voice.Agent, ses instructions dans le contexte de chat de chaque requête. Le plugin les supprime, car les instructions propres à l’agent Mastra côté serveur font autorité. Pour modifier le prompt, modifiez l’agent Mastra.
Tours interrompusLien direct vers Tours interrompus
Lorsque l’utilisateur interrompt une réponse :
- Le plugin annule le flux. Le serveur interrompt la génération et ne conserve rien de ce tour.
- LiveKit enregistre dans son contexte de chat la partie effectivement entendue par l’utilisateur, en la marquant comme interrompue.
- Au tour suivant, le plugin renvoie ce fragment entendu uniquement, placé avant le nouveau message utilisateur, afin que le thread de mémoire soit complété rétroactivement pour correspondre à l’appel. Les messages contiennent les identifiants de message LiveKit et le serveur élimine les doublons par identifiant ; les nouvelles tentatives et les renvois restent donc idempotents.
Si un utilisateur raccroche immédiatement après avoir interrompu la réponse, ce dernier fragment n’est pas enregistré. Lorsque la transcription doit l’inclure, effectuez immédiatement la réconciliation à partir de l’événement de session ; grâce à l’identifiant de message partagé, le renvoi au tour suivant effectue une mise à jour ou une insertion plutôt qu’une duplication :
import { voice } from '@livekit/agents'
import { MastraClient } from '@mastra/client-js'
const client = new MastraClient({ baseUrl: process.env.MASTRA_URL! })
session.on(voice.AgentSessionEventTypes.ConversationItemAdded, ({ item }) => {
if (item.type !== 'message' || item.role !== 'assistant' || !item.interrupted) return
void client.saveMessageToMemory({
agentId: 'support',
messages: [
{
id: item.id,
threadId: callId,
resourceId: userId,
role: 'assistant',
content: item.textContent ?? '',
type: 'text',
createdAt: new Date(),
},
],
})
})
Métriques d’utilisationLien direct vers Métriques d’utilisation
Lorsque le serveur communique l’utilisation des tokens pour un tour, le plugin la transmet à LiveKit. Les événements metrics_collected de la session contiennent ainsi le délai avant le premier token, la durée et le nombre de tokens, comme pour tout plugin LLM. Le même objet d’utilisation (promptTokens, completionTokens, promptCachedTokens, totalTokens) est transmis à onTurnComplete sous la forme result.usage.
Erreurs et délais d’expirationLien direct vers Erreurs et délais d’expiration
Le transport lève les types APIError de LiveKit (APIStatusError, APIConnectionError, APITimeoutError), de sorte que la politique de nouvelle tentative de la session (connOptions.maxRetry) et le basculement de FallbackAdapter fonctionnent sans modification. Un tour n’est jamais retenté après son premier token : mieux vaut qu’une réponse vocale échoue rapidement plutôt qu’elle soit rejouée après avoir été entendue en partie.
Un mécanisme de surveillance de la connexion et du premier token utilise le connOptions.timeoutMs de la session (10 secondes par défaut). Ainsi, un serveur qui accepte la connexion sans jamais diffuser de contenu ne peut pas provoquer un silence indéfini.
Si le serveur Mastra tombe en panne pendant un appel, chaque tentative de réponse échoue avec une erreur typée après ses nouvelles tentatives, et LiveKit ferme la session après plusieurs échecs consécutifs. Rétablissez le serveur avant d’épuiser ce quota pour que l’appel reprenne au tour suivant.
Contenu des messagesLien direct vers Contenu des messages
L’extraction des messages est limitée au texte : le contenu des images est ignoré et le contenu audio est inclus uniquement par l’intermédiaire de sa transcription. Les pipelines vocaux ne sont pas affectés, mais les éléments que vous injectez vous-même dans le contexte de chat doivent contenir du texte.
createRemoteAgentReplyGenerator()Lien direct vers createremoteagentreplygenerator
Construit un générateur de réponses qui exécute la boucle de l’agent sur un serveur Mastra distant via HTTP/SSE. MastraLLM l’utilise en interne dans son mode remote. Utilisez-le directement par l’intermédiaire de createLiveKitWorker et de son option generate pour exécuter le worker complet avec un serveur distant :
import { createLiveKitWorker, createRemoteAgentReplyGenerator } from '@mastra/livekit/worker'
import { mastra } from './index'
export default createLiveKitWorker({
mastra, // local instance for logger and worker config; replies come from the remote server
generate: createRemoteAgentReplyGenerator({
baseUrl: process.env.MASTRA_URL!,
agentId: 'support',
}),
memory: ({ metadata, roomName }) => ({ thread: metadata.threadId ?? roomName }),
stt: 'deepgram/nova-3',
tts: 'cartesia/sonic-3',
})
Sur le chemin generate, les options toolFeedback et onTurnComplete au niveau du worker ne s’appliquent pas, et la détection de fin d’appel du worker ne se déclenche pas ; transmettez plutôt les hooks au générateur.
L’annulation d’un tour (interruption) met fin à la requête HTTP, ce qui annule la génération sur le serveur. Les erreurs sont levées sous forme de types APIError LiveKit. L’option retries s’applique uniquement aux tentatives de connexion initiales. Un tour n’est jamais retenté après son premier fragment.
Renvoie : VoiceReplyGenerator.
OptionsLien direct vers Options
baseUrl:
agentId:
apiPrefix?:
headers?:
fetch?:
timeoutMs?:
retries?:
body?:
toolFeedback?:
onToolCall?:
onTurnComplete?:
speakGreeting()Lien direct vers speakgreeting
Prononce un message d’accueil dans une session que vous gérez, en respectant les options d’interruption et de lecture. Renvoie le SpeechHandle LiveKit, ou undefined en l’absence de texte d’accueil. createLiveKitWorker() l’utilise en interne pour sa configuration greeting.
import { speakGreeting } from '@mastra/livekit/worker'
await speakGreeting(session, {
text: "You've reached support. You're speaking with an AI assistant.",
allowInterruptions: false,
awaitPlayout: true,
})
ParamètresLien direct vers Paramètres
session:
greeting:
waitForAgentDoneSpeaking()Lien direct vers waitforagentdonespeaking
Est résolue dès que l’agent ne produit ni ne lit plus de réponse : son état a quitté thinking et speaking. Elle est résolue immédiatement si l’agent est déjà inactif et, par sécurité, toujours dans le délai maxWaitMs (30 secondes par défaut). Utilisez-la avant de mettre fin à une session pour que les derniers mots soient lus au lieu d’être coupés.
import { waitForAgentDoneSpeaking } from '@mastra/livekit/worker'
await waitForAgentDoneSpeaking(session)
runEndCall()Lien direct vers runendcall
Met fin à l’appel lorsque l’agent demande à raccrocher. Cette fonction attend les derniers mots de l’agent et prononce, sans interruption, un message final facultatif. Elle supprime ensuite la salle et raccroche l’appelant, y compris les appelants SIP. La tâche s’arrête avec ses callbacks enregistrés.
Associez-la à MastraLLM par l’intermédiaire de son onToolCall et à un outil de fin d’appel sur l’agent côté serveur afin de recréer le raccrochage initié par l’agent dans une session que vous gérez :
import { MastraLLM } from '@mastra/livekit/plugin'
import { DEFAULT_END_CALL_TOOL, runEndCall } from '@mastra/livekit/worker'
let ending = false
const llm = new MastraLLM({
remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'support' },
onToolCall: ({ toolName }) => {
if (toolName !== DEFAULT_END_CALL_TOOL || ending) return
ending = true
void runEndCall(session, ctx, {}, console)
},
})
Les constantes exportées DEFAULT_END_CALL_TOOL ('endCall'), DEFAULT_END_CALL_REASON et DEFAULT_END_CALL_MAX_WAIT_MS (30000) contiennent les valeurs par défaut.
ParamètresLien direct vers Paramètres
session:
ctx:
config:
logger:
createEndCallTool()Lien direct vers createendcalltool
Construit l’outil Mastra qu’un agent appelle lorsqu’il souhaite mettre fin à l’appel. L’outil signale cette intention et peut effectuer des opérations de suivi facultatives. Le worker réalise le raccrochage proprement dit. L’outil se trouve dans le point d’entrée racine utilisable en toute sécurité côté serveur. Ajoutez-le aux agents définis dans le code serveur.
import { Agent } from '@mastra/core/agent'
import { createEndCallTool } from '@mastra/livekit'
const supportAgent = new Agent({
id: 'support',
name: 'Support',
instructions:
'Help the caller. When everything is wrapped up, say goodbye and call endCall as your final action.',
model: 'openai/gpt-5-mini',
tools: { endCall: createEndCallTool() },
})
Avec createLiveKitWorker(), définissez configuration: { endCall: {} } : le worker surveille alors l’outil et raccroche. Dans une session que vous gérez, recréez le raccrochage avec runEndCall().
OptionsLien direct vers Options
id?:
description?:
onEndCall?:
liveKitConnectionRoute()Lien direct vers livekitconnectionroute
Renvoie une route d’API qui génère un token d’accès LiveKit et dispatche l’agent vocal dans la salle. Les frontends l’appellent pour rejoindre une session.
import { Mastra } from '@mastra/core/mastra'
import { liveKitConnectionRoute } from '@mastra/livekit'
export const mastra = new Mastra({
server: {
apiRoutes: [liveKitConnectionRoute({ agentName: 'mastra-voice' })],
},
})
La route accepte un corps JSON comportant les champs facultatifs agentId, threadId et resourceId, et répond avec { serverUrl, roomName, participantName, participantToken }. Par défaut, le threadId correspond au nom de salle généré.
OptionsLien direct vers Options
path?:
serverUrl?:
apiKey?:
apiSecret?:
agentName?:
ttl?:
requiresAuth?:
roomName?:
participantIdentity?:
metadata?:
dispatchVoiceSession()Lien direct vers dispatchvoicesession
Dispatche par programmation un agent vocal Mastra dans une salle LiveKit, pour les sessions initiées par le serveur telles que les appels sortants.
import { dispatchVoiceSession } from '@mastra/livekit'
await dispatchVoiceSession({
roomName: 'support-call-42',
agentName: 'mastra-voice',
metadata: { agentId: 'support', threadId: 'thread-42' },
})
OptionsLien direct vers Options
roomName:
agentName?:
metadata?:
serverUrl?:
apiKey?:
apiSecret?:
LiveKitSessionMetadataLien direct vers livekitsessionmetadata
Métadonnées transmises du serveur Mastra au worker par le dispatch de tâche LiveKit.
agentId?:
threadId?:
resourceId?:
requestContext?:
Les métadonnées sont transmises sous forme de chaîne JSON. liveKitConnectionRoute() et dispatchVoiceSession() les sérialisent pour vous ; utilisez serializeSessionMetadata(metadata) lorsque vous effectuez le dispatch depuis votre propre code, ou écrivez directement le JSON dans une configuration côté LiveKit, telle qu’une règle de dispatch SIP. Les entrées de requestContext sont accessibles à chaque tour de l’appel dans les instructions définies à l’exécution, les outils et les processeurs d’entrée de l’agent.