Aller au contenu principal

Utiliser AI SDK UI

AI SDK UI est une bibliothèque d'utilitaires et de composants React permettant de créer des interfaces alimentées par l'IA. Dans ce guide, vous apprendrez à utiliser @mastra/ai-sdk pour convertir les sorties de Mastra dans des formats compatibles avec AI SDK, afin d'employer ses hooks et ses composants dans votre frontend.

remarque

Vous migrez d'AI SDK v4 vers v5 ? Consultez le guide de migration.

astuce

Vous souhaitez voir davantage d'exemples ? Consultez le UI Dojo de Mastra ou le guide de démarrage rapide Next.js.

Bien démarrer
Lien direct vers Bien démarrer

Utilisez Mastra et AI SDK UI ensemble en installant le package @mastra/ai-sdk. @mastra/ai-sdk fournit des routes d'API personnalisées et des utilitaires permettant de diffuser les agents Mastra dans des formats compatibles avec AI SDK. Il comprend des gestionnaires de routes pour le chat, les workflows et les réseaux, ainsi que des utilitaires et des types exportés destinés aux intégrations d'interface utilisateur.

@mastra/ai-sdk s'intègre aux trois principaux hooks d'AI SDK UI : useChat(), useCompletion() et useObject().

Pour commencer, installez les packages requis :

npm install @mastra/ai-sdk@latest @ai-sdk/react ai

Vous pouvez maintenant suivre les guides d'intégration et les recettes ci-dessous !

Guides d'intégration
Lien direct vers Guides d'intégration

En général, vous configurez des routes d'API qui diffusent le contenu Mastra dans un format compatible avec AI SDK, puis utilisez ces routes dans des hooks AI SDK UI tels que useChat(). Choisissez l'une des approches suivantes :

Une fois vos routes d'API configurées, vous pouvez les utiliser dans le hook useChat().

Serveur Mastra
Lien direct vers Serveur Mastra

Exécutez Mastra comme serveur autonome et connectez votre frontend (par exemple avec Vite + React) à ses points de terminaison d'API. Pour cela, vous utiliserez la fonctionnalité de routes d'API personnalisées de Mastra.

info

Le UI Dojo de Mastra illustre cette configuration.

Vous pouvez utiliser chatRoute(), workflowRoute() et networkRoute() pour créer des routes d'API qui diffusent le contenu Mastra dans un format compatible avec AI SDK. Une fois mises en œuvre, ces routes d'API peuvent être utilisées dans useChat().

Cet exemple montre comment configurer, sur le point de terminaison /chat, une route de chat qui utilise un agent portant l'ID weatherAgent.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { chatRoute } from '@mastra/ai-sdk'

export const mastra = new Mastra({
server: {
apiRoutes: [
chatRoute({
path: '/chat',
agent: 'weatherAgent',
}),
],
},
})

Vous pouvez également utiliser le routage dynamique des agents. Consultez la documentation de référence de chatRoute() pour en savoir plus.

Indépendante du framework
Lien direct vers Indépendante du framework

Si vous ne souhaitez pas exécuter le serveur Mastra et préférez utiliser des frameworks comme Next.js ou Express, vous pouvez employer les fonctions handleChatStream(), handleWorkflowStream() et handleNetworkStream() dans vos propres gestionnaires de routes d'API.

Elles renvoient un ReadableStream que vous pouvez encapsuler avec createUIMessageStreamResponse().

Compatibilité avec AI SDK v6

Les gestionnaires indépendants du framework conservent le comportement existant d'AI SDK v5, utilisé par défaut. Si votre application est typée avec AI SDK v6, transmettez version: 'v6'. Pour bénéficier de la meilleure inférence TypeScript avec handleChatStream() et handleNetworkStream(), transmettez messages sous la forme UIMessage[] provenant de la version de ai installée.

Les exemples ci-dessous montrent comment les utiliser avec Next.js App Router.

Cet exemple montre comment configurer, sur le point de terminaison /chat, une route de chat qui utilise un agent portant l'ID weatherAgent.

app/chat/route.ts
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'

export async function POST(req: Request) {
const params = await req.json()
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params,
})
return createUIMessageStreamResponse({ stream })
}

useChat()
Lien direct vers usechat

Que vous ayez créé des routes d'API avec le serveur Mastra ou utilisé le framework de votre choix, vous pouvez désormais employer les points de terminaison de l'API dans le hook useChat().

En supposant que vous ayez configuré sur /chat une route utilisant un agent météo, vous pouvez lui poser des questions comme dans l'exemple ci-dessous. Veillez à définir correctement l'URL api.

import { useChat } from '@ai-sdk/react'
import { useState } from 'react'
import { DefaultChatTransport } from 'ai'

export default function Chat() {
const [inputValue, setInputValue] = useState('')
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat',
}),
})

const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault()
sendMessage({ text: inputValue })
}

return (
<div>
<pre>{JSON.stringify(messages, null, 2)}</pre>
<form onSubmit={handleFormSubmit}>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Name of the city"
/>
</form>
</div>
)
}

Utilisez prepareSendMessagesRequest pour personnaliser la requête envoyée à la route de chat, par exemple afin de transmettre une configuration supplémentaire à l'agent.

Utiliser la Memory de Mastra
Lien direct vers Utiliser la Memory de Mastra

Lorsque la memory de votre agent est configurée, Mastra charge l'historique des conversations depuis le stockage sur le serveur. Depuis le client, envoyez uniquement le nouveau message plutôt que l'intégralité de l'historique.

L'envoi de l'historique complet est redondant et peut provoquer des erreurs d'ordre des messages, car les horodatages côté client peuvent entrer en conflit avec ceux stockés dans votre base de données.

import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'

const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/weatherAgent',
prepareSendMessagesRequest({ messages }) {
return {
body: {
messages: [messages[messages.length - 1]],
memory: {
thread: 'user-thread-123',
resource: 'user-123',
},
},
}
},
}),
})

Définissez memory.thread et memory.resource à partir de l'état propre à votre application, par exemple les paramètres d'URL, le contexte d'authentification ou votre base de données.

Consultez la section Historique des messages pour en savoir plus sur la façon dont la Memory de Mastra charge et stocke les messages.

chatRoute() et handleChatStream() fonctionnent déjà avec la Memory. Configurez le client pour qu'il envoie uniquement le nouveau message et inclue les identifiants de thread et de ressource.

useCompletion()
Lien direct vers usecompletion

Le hook useCompletion() gère les complétions à un seul tour entre votre frontend et un agent Mastra, ce qui vous permet d'envoyer un prompt et de recevoir une réponse diffusée via HTTP.

Votre frontend pourrait ressembler à ceci :

app/page.tsx
import { useCompletion } from '@ai-sdk/react'

export default function Page() {
const { completion, input, handleInputChange, handleSubmit } = useCompletion({
api: '/api/completion',
})

return (
<form onSubmit={handleSubmit}>
<input name="prompt" value={input} onChange={handleInputChange} id="input" />
<button type="submit">Submit</button>
<div>{completion}</div>
</form>
)
}

Choisissez une implémentation backend :

src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { registerApiRoute } from '@mastra/core/server'
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/completion', {
method: 'POST',
handler: async c => {
const { prompt } = await c.req.json()
const mastra = c.get('mastra')
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params: {
messages: [
{
id: '1',
role: 'user',
parts: [
{
type: 'text',
text: prompt,
},
],
},
],
},
})

return createUIMessageStreamResponse({ stream })
},
}),
],
},
})

Interface utilisateur personnalisée
Lien direct vers Interface utilisateur personnalisée

Custom UI (également appelée Generative UI) permet d'afficher des composants React personnalisés à partir de données diffusées par Mastra. Au lieu d'afficher du texte brut ou du JSON, vous pouvez créer des composants visuels pour les sorties de tools et la progression des workflows, notamment l'exécution des réseaux d'agents et les événements personnalisés.

Utilisez Custom UI lorsque vous souhaitez :

  • Afficher les sorties de tools sous forme de composants visuels (par exemple, une carte météo plutôt que du JSON)
  • Afficher la progression des étapes d'un workflow à l'aide d'indicateurs d'état
  • Visualiser l'exécution d'un réseau d'agents avec des mises à jour étape par étape
  • Afficher des indicateurs de progression ou des mises à jour d'état pendant les opérations de longue durée

Types de parties de données
Lien direct vers Types de parties de données

Mastra diffuse les données vers le frontend sous forme de « parties » au sein des messages. Chaque partie possède un type qui détermine son rendu. Le package @mastra/ai-sdk transforme les streams Mastra en UI Message DataParts compatibles avec AI SDK.

Type de partie de donnéesSourceDescription
tool-{toolKey}Fonctionnalité intégrée d'AI SDKAppel de Tool avec les états : input-available, output-available, output-error
data-workflowworkflowRoute()Instantanés de l'état d'exécution du workflow avec l'état des étapes et les sorties finales
data-workflow-stepworkflowRoute()Delta d'une étape de workflow avec le payload complet de l'étape modifiée
data-networknetworkRoute()Exécution du réseau d'agents avec les étapes et sorties ordonnées
data-tool-agentAgent imbriqué dans un ToolInstantané compact de l'agent imbriqué pendant que l'étape courante est encore en cours
data-tool-agent-stepAgent imbriqué dans un ToolPayload complet de l'étape de l'agent imbriqué émis à la fin d'une étape imbriquée
data-tool-workflowWorkflow imbriqué dans un ToolSortie du workflow diffusée depuis la fonction execute() d'un Tool
data-tool-networkRéseau imbriqué dans un ToolSortie du réseau diffusée depuis la fonction execute() d'un Tool
data-{custom}writer.custom()Événements personnalisés pour les indicateurs de progression, les mises à jour d'état, etc.

Afficher les sorties des Tools
Lien direct vers Afficher les sorties des Tools

AI SDK crée automatiquement des parties tool-{toolKey} lorsqu'un agent appelle un Tool. Ces parties comprennent l'état et la sortie du Tool, que vous pouvez utiliser pour afficher des composants personnalisés.

La partie du Tool passe successivement par les états suivants :

  • input-streaming : l'entrée du Tool est en cours de diffusion (lorsque le streaming des appels de Tools est activé)
  • input-available : le Tool a été appelé avec une entrée complète et attend son exécution
  • output-available : l'exécution du Tool est terminée et a produit une sortie
  • output-error : l'exécution du Tool a échoué

Voici un exemple de rendu de la sortie d'un Tool météo sous forme de composant WeatherCard personnalisé.

Définissez un Tool avec un outputSchema afin que le frontend connaisse la structure des données à afficher.

src/mastra/tools/weather-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
}),
outputSchema: z.object({
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
conditions: z.string(),
location: z.string(),
}),
execute: async inputData => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${inputData.location}`,
)
const data = await response.json()
return {
temperature: data.current.temp_c,
feelsLike: data.current.feelslike_c,
humidity: data.current.humidity,
windSpeed: data.current.wind_kph,
conditions: data.current.condition.text,
location: data.location.name,
}
},
})
astuce

Le type de partie du Tool suit le modèle tool-{toolKey}, où toolKey est la clé utilisée lors de l'enregistrement du Tool auprès de l'agent. Par exemple, si vous enregistrez des Tools sous la forme tools: { weatherTool }, le type de la partie sera tool-weatherTool.

Afficher les données de workflow
Lien direct vers Afficher les données de workflow

Lorsque vous utilisez workflowRoute() ou handleWorkflowStream(), Mastra émet des parties data-workflow pour les instantanés de l'état du workflow et des parties data-workflow-step pour le payload complet de l'étape modifiée. Les workflows de longue durée évitent ainsi de répéter la sortie de chaque étape terminée dans tous les instantanés intermédiaires.

Définissez un workflow en plusieurs étapes qui émettra des parties data-workflow et data-workflow-step au fil de son exécution.

src/mastra/workflows/activities-workflow.ts
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'

const fetchWeather = createStep({
id: 'fetch-weather',
inputSchema: z.object({
location: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
}),
execute: async ({ inputData }) => {
// Fetch weather data...
return { temperature: 22, conditions: 'Sunny' }
},
})

const planActivities = createStep({
id: 'plan-activities',
inputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
}),
outputSchema: z.object({
activities: z.string(),
}),
execute: async ({ inputData, mastra }) => {
const agent = mastra?.getAgent('activityAgent')
const response = await agent?.generate(
`Suggest activities for ${inputData.conditions} weather at ${inputData.temperature}°C`,
)
return { activities: response?.text || '' }
},
})

export const activitiesWorkflow = createWorkflow({
id: 'activities-workflow',
inputSchema: z.object({
location: z.string(),
}),
outputSchema: z.object({
activities: z.string(),
}),
})
.then(fetchWeather)
.then(planActivities)

activitiesWorkflow.commit()

Enregistrez le workflow auprès de Mastra et exposez-le avec workflowRoute() afin de diffuser les événements du workflow vers le frontend.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'

export const mastra = new Mastra({
workflows: { activitiesWorkflow },
server: {
apiRoutes: [
workflowRoute({
path: '/workflow/activitiesWorkflow',
workflow: 'activitiesWorkflow',
}),
],
},
})

Pour en savoir plus sur le streaming des workflows, consultez la section Streaming des workflows.

Afficher les données de réseau
Lien direct vers Afficher les données de réseau

Lorsque vous utilisez networkRoute() ou handleNetworkStream(), Mastra émet des parties data-network qui contiennent l'état d'exécution du réseau d'agents, notamment les agents appelés et leurs sorties.

Enregistrez les agents auprès de Mastra et exposez l'agent de routage avec networkRoute() afin de diffuser les événements d'exécution du réseau vers le frontend.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'

export const mastra = new Mastra({
agents: { routingAgent, researchAgent, weatherAgent },
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'routingAgent',
}),
],
},
})

Pour en savoir plus sur les réseaux d'agents, consultez la section Réseaux d'agents.

Événements personnalisés
Lien direct vers Événements personnalisés

Utilisez writer.custom() dans la fonction execute() d'un Tool afin d'émettre des parties de données personnalisées. Cette méthode est utile pour les indicateurs de progression, les mises à jour d'état ou toute mise à jour personnalisée de l'interface utilisateur pendant l'exécution du Tool.

Les types d'événements personnalisés doivent commencer par data- pour être reconnus comme des parties de données.

attention

Vous devez utiliser await lors de l'appel à writer.custom(), faute de quoi vous risquez de rencontrer une erreur WritableStream is locked.

Utilisez writer.custom() dans la fonction execute() du Tool afin d'émettre, à différentes étapes de l'exécution, des événements personnalisés préfixés par data-.

src/mastra/tools/task-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const taskTool = createTool({
id: 'process-task',
description: 'Process a task with progress updates',
inputSchema: z.object({
task: z.string().describe('The task to process'),
}),
outputSchema: z.object({
result: z.string(),
status: z.string(),
}),
execute: async (inputData, context) => {
const { task } = inputData

// Emit "in progress" custom event
await context?.writer?.custom({
type: 'data-tool-progress',
data: {
status: 'in-progress',
message: 'Gathering information...',
},
})

// Simulate work
await new Promise(resolve => setTimeout(resolve, 3000))

// Emit "done" custom event
await context?.writer?.custom({
type: 'data-tool-progress',
data: {
status: 'done',
message: `Successfully processed "${task}"`,
},
})

return {
result: `Task "${task}" has been completed successfully!`,
status: 'completed',
}
},
})

Streaming des Tools
Lien direct vers Streaming des Tools

Les Tools peuvent également diffuser des données à l'aide de context.writer.write() pour un contrôle de plus bas niveau, ou rediriger directement le stream d'un agent vers le writer du Tool. Pour en savoir plus, consultez la section Streaming des Tools.

Exemples
Lien direct vers Exemples

Pour voir des exemples interactifs de modèles Custom UI, consultez le UI Dojo de Mastra. Le dépôt contient des implémentations pour :

Recettes
Lien direct vers Recettes

Transformer les streams
Lien direct vers Transformer les streams

Pour transformer manuellement les streams Mastra dans un format compatible avec AI SDK, utilisez l'utilitaire toAISdkStream(). Consultez les exemples pour découvrir des modèles d'utilisation concrets.

toAISdkStream() conserve le comportement existant d'AI SDK v5, utilisé par défaut. Si votre application est typée avec AI SDK v6, transmettez version: 'v6'.

import { toAISdkStream } from '@mastra/ai-sdk'

const v5Stream = toAISdkStream(mastraStream, { from: 'agent' })
const v6Stream = toAISdkStream(mastraStream, { from: 'agent', version: 'v6' })

Charger les messages historiques
Lien direct vers Charger les messages historiques

Lorsque vous chargez des messages depuis la Memory de Mastra pour les afficher dans une interface de chat, utilisez toAISdkV5Messages() ou toAISdkV4Messages() afin de les convertir au format AI SDK adapté à useChat(), dans sa propriété initialMessages.

Transmettre des données supplémentaires
Lien direct vers Transmettre des données supplémentaires

sendMessage() permet de transmettre des données supplémentaires du frontend à Mastra. Ces données peuvent ensuite être utilisées sur le serveur sous la forme d'un RequestContext.

Voici un exemple de code frontend :

import { useChat } from '@ai-sdk/react'
import { useState } from 'react'
import { DefaultChatTransport } from 'ai'

export function ChatAdditional() {
const [inputValue, setInputValue] = useState('')
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat-extra',
}),
})

const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault()
sendMessage(
{ text: inputValue },
{
body: {
data: {
userId: 'user123',
preferences: {
language: 'en',
temperature: 'celsius',
},
},
},
},
)
}

return (
<div>
<pre>{JSON.stringify(messages, null, 2)}</pre>
<form onSubmit={handleFormSubmit}>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Name of the city"
/>
</form>
</div>
)
}

Implémentez le backend à l'aide de l'un des exemples suivants.

Ajoutez une chatRoute() à votre configuration Mastra comme illustré ci-dessus. Ajoutez ensuite un middleware au niveau du serveur :

src/mastra/index.ts
import { Mastra } from '@mastra/core'

export const mastra = new Mastra({
server: {
middleware: [
async (c, next) => {
const requestContext = c.get('requestContext')

if (c.req.method === 'POST') {
const clonedReq = c.req.raw.clone()
const body = await clonedReq.json()

if (body?.data) {
for (const [key, value] of Object.entries(body.data)) {
requestContext.set(key, value)
}
}
}
await next()
},
],
},
})
info

Vous pouvez accéder à ces données dans vos Tools à l'aide du paramètre requestContext. Consultez la documentation sur Request Context pour en savoir plus.

Suspendre et reprendre un workflow avec l'approbation de l'utilisateur
Lien direct vers Suspendre et reprendre un workflow avec l'approbation de l'utilisateur

Les workflows peuvent suspendre leur exécution et attendre une saisie de l'utilisateur avant de continuer. Cette fonctionnalité est utile pour les processus d'approbation, les confirmations ou tout scénario impliquant une intervention humaine.

Le workflow utilise les éléments suivants :

  • suspendSchema / resumeSchema : définissent la structure des données du payload de suspension et de l'entrée de reprise
  • suspend() : suspend le workflow et envoie le payload de suspension à l'interface utilisateur
  • resumeData : contient la réponse de l'utilisateur lors de la reprise du workflow
  • bail() : met fin au workflow de façon anticipée (par exemple, lorsque l'utilisateur refuse)

Créez une étape de workflow qui se suspend dans l'attente d'une approbation. L'étape examine resumeData pour déterminer s'il s'agit d'une reprise et appelle suspend() lors de la première exécution.

src/mastra/workflows/approval-workflow.ts
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'

const requestApproval = createStep({
id: 'request-approval',
inputSchema: z.object({ requestId: z.string(), summary: z.string() }),
outputSchema: z.object({
approved: z.boolean(),
requestId: z.string(),
approvedBy: z.string().optional(),
}),
resumeSchema: z.object({
approved: z.boolean(),
approverName: z.string().optional(),
}),
suspendSchema: z.object({
message: z.string(),
requestId: z.string(),
}),
execute: async ({ inputData, resumeData, suspend, bail }) => {
// User rejected - bail out
if (resumeData?.approved === false) {
return bail({ message: 'Request rejected' })
}
// User approved - continue
if (resumeData?.approved) {
return {
approved: true,
requestId: inputData.requestId,
approvedBy: resumeData.approverName || 'User',
}
}
// First execution - suspend and wait
return await suspend({
message: `Please approve: ${inputData.summary}`,
requestId: inputData.requestId,
})
},
})

export const approvalWorkflow = createWorkflow({
id: 'approval-workflow',
inputSchema: z.object({ requestId: z.string(), summary: z.string() }),
outputSchema: z.object({
approved: z.boolean(),
requestId: z.string(),
approvedBy: z.string().optional(),
}),
}).then(requestApproval)

approvalWorkflow.commit()

Enregistrez le workflow. Un stockage est nécessaire pour conserver l'état lors des opérations de suspension et de reprise.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'
import { LibSQLStore } from '@mastra/libsql'

export const mastra = new Mastra({
workflows: { approvalWorkflow },
storage: new LibSQLStore({
id: 'mastra-storage',
url: 'file:../mastra.db',
}),
server: {
apiRoutes: [
workflowRoute({ path: '/workflow/approvalWorkflow', workflow: 'approvalWorkflow' }),
],
},
})

Points essentiels :

  • Le payload de suspension est accessible via step.suspendPayload
  • Pour reprendre le workflow, envoyez runId, step (l'ID de l'étape) et resumeData dans le corps de la requête
  • Le stockage doit être configuré afin de conserver l'état du workflow lors des opérations de suspension et de reprise

Pour consulter une implémentation complète, reportez-vous à l'exemple workflow-suspend-resume dans UI Dojo.

Streams d'agents imbriqués dans des Tools
Lien direct vers Streams d'agents imbriqués dans des Tools

Les Tools peuvent appeler des agents en interne et rediffuser la sortie de l'agent vers l'interface utilisateur. Cela crée des instantanés data-tool-agent compacts tant que l'étape imbriquée est encore en cours, des parties data-tool-agent-step lorsqu'une étape imbriquée se termine et un instantané data-tool-agent complet à la fin de l'exécution imbriquée.

Ce modèle utilise les éléments suivants :

  • context.mastra.getAgent() : récupère une instance d'agent depuis un Tool
  • agent.stream() : diffuse la réponse de l'agent
  • stream.fullStream.pipeTo(context.writer) : redirige le stream de l'agent vers le writer du Tool

Créez un Tool qui appelle un agent et redirige son stream vers le writer du Tool.

src/mastra/tools/nested-agent-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const nestedAgentTool = createTool({
id: 'nested-agent-stream',
description: 'Analyze weather using a nested agent',
inputSchema: z.object({
city: z.string().describe('The city to analyze'),
}),
outputSchema: z.object({
summary: z.string(),
}),
execute: async (inputData, context) => {
const agent = context?.mastra?.getAgent('weatherAgent')
if (!agent) {
return { summary: 'Weather agent not available' }
}

const stream = await agent.stream(
`Analyze the weather in ${inputData.city} and provide a summary.`,
)

// Pipe the agent's stream to emit data-tool-agent parts
await stream.fullStream.pipeTo(context!.writer!)

return { summary: (await stream.text) ?? 'No summary available' }
},
})

Créez un agent qui utilise ce Tool.

src/mastra/agents/forecast-agent.ts
import { Agent } from '@mastra/core/agent'
import { nestedAgentTool } from '../tools/nested-agent-tool'

export const forecastAgent = new Agent({
id: 'forecast-agent',
instructions: 'Use the nested-agent-stream tool when asked about weather.',
model: 'openai/gpt-5.6-sol',
tools: { nestedAgentTool },
})

Points essentiels :

  • Rediriger fullStream vers context.writer crée des parties data-tool-agent
  • Lisez data-tool-agent-step lorsque vous avez besoin du payload complet de l'étape imbriquée qui vient de se terminer
  • AgentDataPart possède id (sur la partie) et data.text (l'instantané de texte actuel de l'agent imbriqué)
  • Le Tool renvoie toujours sa propre sortie une fois le stream terminé

Pour consulter une implémentation complète, reportez-vous à l'exemple tool-nested-streams dans UI Dojo.

Diffuser le texte d'un agent depuis les étapes d'un workflow
Lien direct vers Diffuser le texte d'un agent depuis les étapes d'un workflow

Les étapes d'un workflow peuvent diffuser la sortie textuelle d'un agent en temps réel en redirigeant le stream de l'agent vers le writer de l'étape. Les utilisateurs peuvent ainsi voir l'agent « réfléchir » pendant l'exécution du workflow, au lieu d'attendre la fin de l'étape.

Ce modèle utilise les éléments suivants :

  • writer dans l'étape du workflow : redirige le fullStream de l'agent vers le writer de l'étape
  • Parties text et data-workflow : le frontend reçoit le texte diffusé en même temps que la progression de l'étape

Créez une étape de workflow qui diffuse la réponse d'un agent en la redirigeant vers le writer de l'étape.

src/mastra/workflows/weather-workflow.ts
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'
import { weatherAgent } from '../agents/weather-agent'

const analyzeWeather = createStep({
id: 'analyze-weather',
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ analysis: z.string(), location: z.string() }),
execute: async ({ inputData, writer }) => {
const response = await weatherAgent.stream(
`Analyze the weather in ${inputData.location} and provide insights.`,
)

// Pipe agent stream to step writer for real-time text streaming
await response.fullStream.pipeTo(writer)

return {
analysis: await response.text,
location: inputData.location,
}
},
})

const calculateScore = createStep({
id: 'calculate-score',
inputSchema: z.object({ analysis: z.string(), location: z.string() }),
outputSchema: z.object({ score: z.number(), summary: z.string() }),
execute: async ({ inputData }) => {
const score = inputData.analysis.includes('sunny') ? 85 : 50
return { score, summary: `Comfort score for ${inputData.location}: ${score}/100` }
},
})

export const weatherWorkflow = createWorkflow({
id: 'weather-workflow',
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ score: z.number(), summary: z.string() }),
})
.then(analyzeWeather)
.then(calculateScore)

weatherWorkflow.commit()

Enregistrez le workflow avec une workflowRoute(). Le streaming du texte est activé par défaut.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'

export const mastra = new Mastra({
agents: { weatherAgent },
workflows: { weatherWorkflow },
server: {
apiRoutes: [workflowRoute({ path: '/workflow/weather', workflow: 'weatherWorkflow' })],
},
})

Points essentiels :

  • Le writer de l'étape est disponible dans la fonction execute (et non via context)
  • includeTextStreamParts vaut true par défaut sur workflowRoute() ; le texte est donc diffusé par défaut
  • Les parties textuelles sont diffusées en temps réel tandis que les parties data-workflow sont mises à jour avec l'état de l'étape

Pour consulter une implémentation complète, reportez-vous à l'exemple workflow-agent-text-stream dans UI Dojo.

Progression en plusieurs étapes avec des workflows à branchements
Lien direct vers Progression en plusieurs étapes avec des workflows à branchements

Pour les workflows comportant des branchements conditionnels (par exemple, livraison express ou standard), vous pouvez suivre la progression dans les différentes branches en incluant un identifiant dans vos événements personnalisés.

L'exemple UI Dojo utilise un champ stage dans les données de l'événement pour identifier la branche en cours d'exécution (par exemple, "validation", "standard-processing", "express-processing"). Le frontend regroupe les événements selon ce champ afin d'afficher une interface de progression sous forme de pipeline.

Consultez les fichiers branching-workflow.ts (backend) et workflow-custom-events.tsx (frontend) dans UI Dojo.

Indicateurs de progression dans les réseaux d'agents
Lien direct vers Indicateurs de progression dans les réseaux d'agents

Lorsque vous utilisez des réseaux d'agents, vous pouvez émettre des événements de progression personnalisés depuis les Tools employés par les sous-agents afin d'indiquer l'agent actuellement actif.

L'exemple UI Dojo inclut un champ stage dans les données de l'événement afin d'identifier le sous-agent en cours d'exécution (par exemple, "report-generation", "report-review"). Le frontend regroupe les événements selon ce champ et affiche l'état le plus récent de chacun.

Consultez les fichiers report-generation-tool.ts (backend) et agent-network-custom-events.tsx (frontend) dans UI Dojo.