@mastra/mcp-docs-server 0.0.5 → 0.0.6-alpha.2
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/organized/changelogs/%40mastra%2Fastra.md +52 -52
- package/.docs/organized/changelogs/%40mastra%2Fchroma.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fclickhouse.md +11 -0
- package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +55 -55
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +52 -52
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloudflare.md +61 -61
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +61 -61
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-vercel.md +61 -61
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +61 -61
- package/.docs/organized/changelogs/%40mastra%2Fevals.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Ffirecrawl.md +55 -55
- package/.docs/organized/changelogs/%40mastra%2Fgithub.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Floggers.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +49 -0
- package/.docs/organized/changelogs/%40mastra%2Fmcp.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fmem0.md +50 -0
- package/.docs/organized/changelogs/%40mastra%2Fmemory.md +61 -61
- package/.docs/organized/changelogs/%40mastra%2Fpg.md +54 -54
- package/.docs/organized/changelogs/%40mastra%2Fpinecone.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +64 -64
- package/.docs/organized/changelogs/%40mastra%2Fqdrant.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Frag.md +52 -52
- package/.docs/organized/changelogs/%40mastra%2Fragie.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +302 -0
- package/.docs/organized/changelogs/%40mastra%2Fspeech-azure.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-deepgram.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-elevenlabs.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-google.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fspeech-ibm.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-murf.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-openai.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-playai.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-replicate.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fspeech-speechify.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fturbopuffer.md +49 -0
- package/.docs/organized/changelogs/%40mastra%2Fupstash.md +50 -50
- package/.docs/organized/changelogs/%40mastra%2Fvectorize.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-azure.md +50 -0
- package/.docs/organized/changelogs/%40mastra%2Fvoice-cloudflare.md +50 -0
- package/.docs/organized/changelogs/%40mastra%2Fvoice-deepgram.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-elevenlabs.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-google.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-murf.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-openai-realtime.md +55 -0
- package/.docs/organized/changelogs/%40mastra%2Fvoice-openai.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-playai.md +51 -51
- package/.docs/organized/changelogs/%40mastra%2Fvoice-sarvam.md +50 -0
- package/.docs/organized/changelogs/%40mastra%2Fvoice-speechify.md +51 -51
- package/.docs/organized/changelogs/create-mastra.md +18 -18
- package/.docs/organized/changelogs/mastra.md +70 -70
- package/.docs/organized/code-examples/agent-network.md +53 -0
- package/.docs/organized/code-examples/crypto-chatbot.md +3 -3
- package/.docs/raw/agents/{02-adding-tools.mdx → adding-tools.mdx} +1 -1
- package/.docs/raw/agents/{03-adding-voice.mdx → adding-voice.mdx} +3 -3
- package/.docs/raw/agents/{00-overview.mdx → overview.mdx} +4 -4
- package/.docs/raw/community/discord.mdx +12 -0
- package/.docs/raw/deployment/server.mdx +66 -11
- package/.docs/raw/evals/{02-custom-eval.mdx → custom-eval.mdx} +1 -1
- package/.docs/raw/evals/{00-overview.mdx → overview.mdx} +3 -3
- package/.docs/raw/evals/{01-textual-evals.mdx → textual-evals.mdx} +1 -1
- package/.docs/raw/frameworks/{02-ai-sdk.mdx → ai-sdk.mdx} +3 -3
- package/.docs/raw/getting-started/installation.mdx +1 -1
- package/.docs/raw/index.mdx +4 -4
- package/.docs/raw/integrations/index.mdx +1 -1
- package/.docs/raw/local-dev/mastra-dev.mdx +1 -1
- package/.docs/raw/observability/nextjs-tracing.mdx +43 -37
- package/.docs/raw/observability/tracing.mdx +7 -2
- package/.docs/raw/rag/chunking-and-embedding.mdx +32 -4
- package/.docs/raw/rag/vector-databases.mdx +2 -0
- package/.docs/raw/reference/client-js/workflows.mdx +4 -6
- package/.docs/raw/reference/memory/Memory.mdx +1 -1
- package/.docs/raw/reference/memory/memory-processors.mdx +229 -0
- package/.docs/raw/reference/rag/astra.mdx +1 -1
- package/.docs/raw/reference/rag/chroma.mdx +1 -1
- package/.docs/raw/reference/rag/chunk.mdx +2 -4
- package/.docs/raw/reference/rag/document.mdx +2 -4
- package/.docs/raw/reference/rag/extract-params.mdx +192 -40
- package/.docs/raw/reference/rag/libsql.mdx +1 -1
- package/.docs/raw/reference/rag/pg.mdx +1 -1
- package/.docs/raw/reference/rag/pinecone.mdx +36 -4
- package/.docs/raw/reference/rag/qdrant.mdx +1 -1
- package/.docs/raw/reference/rag/turbopuffer.mdx +1 -1
- package/.docs/raw/reference/rag/upstash.mdx +1 -1
- package/.docs/raw/reference/rag/vectorize.mdx +1 -1
- package/.docs/raw/reference/storage/postgresql.mdx +1 -1
- package/.docs/raw/reference/workflows/createRun.mdx +1 -1
- package/.docs/raw/reference/workflows/resumeWithEvent.mdx +23 -24
- package/.docs/raw/reference/workflows/snapshots.mdx +36 -33
- package/.docs/raw/reference/workflows/suspend.mdx +0 -10
- package/.docs/raw/reference/workflows/watch.mdx +22 -22
- package/.docs/raw/storage/overview.mdx +97 -59
- package/.docs/raw/voice/overview.mdx +1 -1
- package/.docs/raw/voice/speech-to-text.mdx +1 -1
- package/.docs/raw/voice/text-to-speech.mdx +1 -1
- package/.docs/raw/voice/voice-to-voice.mdx +1 -1
- package/.docs/raw/workflows/error-handling.mdx +3 -3
- package/.docs/raw/workflows/nested-workflows.mdx +352 -0
- package/.docs/raw/workflows/{00-overview.mdx → overview.mdx} +1 -1
- package/.docs/raw/workflows/suspend-and-resume.mdx +73 -70
- package/package.json +4 -4
- package/.docs/raw/agents/02b-discord-mcp-bot.mdx +0 -87
- /package/.docs/raw/agents/{01-agent-memory.mdx → agent-memory.mdx} +0 -0
- /package/.docs/raw/agents/{02a-mcp-guide.mdx → mcp-guide.mdx} +0 -0
- /package/.docs/raw/{faq/index.mdx → community/licensing.mdx} +0 -0
- /package/.docs/raw/evals/{03-running-in-ci.mdx → running-in-ci.mdx} +0 -0
- /package/.docs/raw/frameworks/{01-next-js.mdx → next-js.mdx} +0 -0
- /package/.docs/raw/guides/{03-recruiter.mdx → ai-recruiter.mdx} +0 -0
- /package/.docs/raw/guides/{01-chef-michel.mdx → chef-michel.mdx} +0 -0
- /package/.docs/raw/guides/{04-research-assistant.mdx → research-assistant.mdx} +0 -0
- /package/.docs/raw/guides/{02-stock-agent.mdx → stock-agent.mdx} +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Nested Workflows
|
|
2
|
+
|
|
3
|
+
Mastra allows you to use workflows as steps within other workflows, enabling you to create modular and reusable workflow components. This feature helps in organizing complex workflows into smaller, manageable pieces and promotes code reuse.
|
|
4
|
+
|
|
5
|
+
It is also visually easier to understand the flow of a workflow when you can see the nested workflows as steps in the parent workflow.
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
You can use a workflow as a step directly in another workflow using the `step()` method:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// Create a nested workflow
|
|
13
|
+
const nestedWorkflow = new Workflow({ name: "nested-workflow" })
|
|
14
|
+
.step(stepA)
|
|
15
|
+
.then(stepB)
|
|
16
|
+
.commit();
|
|
17
|
+
|
|
18
|
+
// Use the nested workflow in a parent workflow
|
|
19
|
+
const parentWorkflow = new Workflow({ name: "parent-workflow" })
|
|
20
|
+
.step(nestedWorkflow, {
|
|
21
|
+
variables: {
|
|
22
|
+
city: {
|
|
23
|
+
step: "trigger",
|
|
24
|
+
path: "myTriggerInput",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
.then(stepC)
|
|
29
|
+
.commit();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
When a workflow is used as a step:
|
|
33
|
+
|
|
34
|
+
- It is automatically converted to a step using the workflow's name as the step ID
|
|
35
|
+
- The workflow's results are available in the parent workflow's context
|
|
36
|
+
- The nested workflow's steps are executed in their defined order
|
|
37
|
+
|
|
38
|
+
## Accessing Results
|
|
39
|
+
|
|
40
|
+
Results from a nested workflow are available in the parent workflow's context under the nested workflow's name. The results include all step outputs from the nested workflow:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const { results } = await parentWorkflow.start();
|
|
44
|
+
// Access nested workflow results
|
|
45
|
+
const nestedWorkflowResult = results["nested-workflow"];
|
|
46
|
+
if (nestedWorkflowResult.status === "success") {
|
|
47
|
+
const nestedResults = nestedWorkflowResult.output.results;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Control Flow with Nested Workflows
|
|
52
|
+
|
|
53
|
+
Nested workflows support all the control flow features available to regular steps:
|
|
54
|
+
|
|
55
|
+
### Parallel Execution
|
|
56
|
+
|
|
57
|
+
Multiple nested workflows can be executed in parallel:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
parentWorkflow
|
|
61
|
+
.step(nestedWorkflowA)
|
|
62
|
+
.step(nestedWorkflowB)
|
|
63
|
+
.after([nestedWorkflowA, nestedWorkflowB])
|
|
64
|
+
.step(finalStep);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or using `step()` with an array of workflows:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
parentWorkflow.step([nestedWorkflowA, nestedWorkflowB]).then(finalStep);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
In this case, `then()` will implicitly wait for all the workflows to finish before executing the final step.
|
|
74
|
+
|
|
75
|
+
### If-Else Branching
|
|
76
|
+
|
|
77
|
+
Nested workflows can be used in if-else branches using the new syntax that accepts both branches as arguments:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Create nested workflows for different paths
|
|
81
|
+
const workflowA = new Workflow({ name: "workflow-a" })
|
|
82
|
+
.step(stepA1)
|
|
83
|
+
.then(stepA2)
|
|
84
|
+
.commit();
|
|
85
|
+
|
|
86
|
+
const workflowB = new Workflow({ name: "workflow-b" })
|
|
87
|
+
.step(stepB1)
|
|
88
|
+
.then(stepB2)
|
|
89
|
+
.commit();
|
|
90
|
+
|
|
91
|
+
// Use the new if-else syntax with nested workflows
|
|
92
|
+
parentWorkflow
|
|
93
|
+
.step(initialStep)
|
|
94
|
+
.if(
|
|
95
|
+
async ({ context }) => {
|
|
96
|
+
// Your condition here
|
|
97
|
+
return someCondition;
|
|
98
|
+
},
|
|
99
|
+
workflowA, // if branch
|
|
100
|
+
workflowB, // else branch
|
|
101
|
+
)
|
|
102
|
+
.then(finalStep)
|
|
103
|
+
.commit();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The new syntax is more concise and clearer when working with nested workflows. When the condition is:
|
|
107
|
+
|
|
108
|
+
- `true`: The first workflow (if branch) is executed
|
|
109
|
+
- `false`: The second workflow (else branch) is executed
|
|
110
|
+
|
|
111
|
+
The skipped workflow will have a status of `skipped` in the results:
|
|
112
|
+
|
|
113
|
+
The `.then(finalStep)` call following the if-else block will merge the if and else branches back into a single execution path.
|
|
114
|
+
|
|
115
|
+
### Looping
|
|
116
|
+
|
|
117
|
+
Nested workflows can use `.until()` and `.while()` loops same as any other step. One interesting new pattern is to pass a workflow directly as the loop-back argument to keep executing that nested workflow until something is true about its results:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
parentWorkflow
|
|
121
|
+
.step(firstStep)
|
|
122
|
+
.while(
|
|
123
|
+
({ context }) =>
|
|
124
|
+
context.getStepResult("nested-workflow").output.results.someField ===
|
|
125
|
+
"someValue",
|
|
126
|
+
nestedWorkflow,
|
|
127
|
+
)
|
|
128
|
+
.step(finalStep)
|
|
129
|
+
.commit();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Watching Nested Workflows
|
|
133
|
+
|
|
134
|
+
You can watch the state changes of nested workflows using the `watch` method on the parent workflow. This is useful for monitoring the progress and state transitions of complex workflows:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const parentWorkflow = new Workflow({ name: "parent-workflow" })
|
|
138
|
+
.step([nestedWorkflowA, nestedWorkflowB])
|
|
139
|
+
.then(finalStep)
|
|
140
|
+
.commit();
|
|
141
|
+
|
|
142
|
+
const run = parentWorkflow.createRun();
|
|
143
|
+
const unwatch = parentWorkflow.watch((state) => {
|
|
144
|
+
console.log("Current state:", state.value);
|
|
145
|
+
// Access nested workflow states in state.context
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
await run.start();
|
|
149
|
+
unwatch(); // Stop watching when done
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Suspending and Resuming
|
|
153
|
+
|
|
154
|
+
Nested workflows support suspension and resumption, allowing you to pause and continue workflow execution at specific points. You can suspend either the entire nested workflow or specific steps within it:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Define a step that may need to suspend
|
|
158
|
+
const suspendableStep = new Step({
|
|
159
|
+
id: "other",
|
|
160
|
+
description: "Step that may need to suspend",
|
|
161
|
+
execute: async ({ context, suspend }) => {
|
|
162
|
+
if (!wasSuspended) {
|
|
163
|
+
wasSuspended = true;
|
|
164
|
+
await suspend();
|
|
165
|
+
}
|
|
166
|
+
return { other: 26 };
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Create a nested workflow with suspendable steps
|
|
171
|
+
const nestedWorkflow = new Workflow({ name: "nested-workflow-a" })
|
|
172
|
+
.step(startStep)
|
|
173
|
+
.then(suspendableStep)
|
|
174
|
+
.then(finalStep)
|
|
175
|
+
.commit();
|
|
176
|
+
|
|
177
|
+
// Use in parent workflow
|
|
178
|
+
const parentWorkflow = new Workflow({ name: "parent-workflow" })
|
|
179
|
+
.step(beginStep)
|
|
180
|
+
.then(nestedWorkflow)
|
|
181
|
+
.then(lastStep)
|
|
182
|
+
.commit();
|
|
183
|
+
|
|
184
|
+
// Start the workflow
|
|
185
|
+
const run = parentWorkflow.createRun();
|
|
186
|
+
const { runId, results } = await run.start({ triggerData: { startValue: 1 } });
|
|
187
|
+
|
|
188
|
+
// Check if a specific step in the nested workflow is suspended
|
|
189
|
+
if (results["nested-workflow-a"].output.results.other.status === "suspended") {
|
|
190
|
+
// Resume the specific suspended step using dot notation
|
|
191
|
+
const resumedResults = await run.resume({
|
|
192
|
+
stepId: "nested-workflow-a.other",
|
|
193
|
+
context: { startValue: 1 },
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// The resumed results will contain the completed nested workflow
|
|
197
|
+
expect(resumedResults.results["nested-workflow-a"].output.results).toEqual({
|
|
198
|
+
start: { output: { newValue: 1 }, status: "success" },
|
|
199
|
+
other: { output: { other: 26 }, status: "success" },
|
|
200
|
+
final: { output: { finalValue: 27 }, status: "success" },
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
When resuming a nested workflow:
|
|
206
|
+
|
|
207
|
+
- Use the nested workflow's name as the `stepId` when calling `resume()` to resume the entire workflow
|
|
208
|
+
- Use dot notation (`nested-workflow.step-name`) to resume a specific step within the nested workflow
|
|
209
|
+
- The nested workflow will continue from the suspended step with the provided context
|
|
210
|
+
- You can check the status of specific steps in the nested workflow's results using `results["nested-workflow"].output.results`
|
|
211
|
+
|
|
212
|
+
## Result Schemas and Mapping
|
|
213
|
+
|
|
214
|
+
Nested workflows can define their result schema and mapping, which helps in type safety and data transformation. This is particularly useful when you want to ensure the nested workflow's output matches a specific structure or when you need to transform the results before they're used in the parent workflow.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Create a nested workflow with result schema and mapping
|
|
218
|
+
const nestedWorkflow = new Workflow({
|
|
219
|
+
name: "nested-workflow",
|
|
220
|
+
result: {
|
|
221
|
+
schema: z.object({
|
|
222
|
+
total: z.number(),
|
|
223
|
+
items: z.array(
|
|
224
|
+
z.object({
|
|
225
|
+
id: z.string(),
|
|
226
|
+
value: z.number(),
|
|
227
|
+
}),
|
|
228
|
+
),
|
|
229
|
+
}),
|
|
230
|
+
mapping: {
|
|
231
|
+
// Map values from step results using variables syntax
|
|
232
|
+
total: { step: "step-a", path: "count" },
|
|
233
|
+
items: { step: "step-b", path: "items" },
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
.step(stepA)
|
|
238
|
+
.then(stepB)
|
|
239
|
+
.commit();
|
|
240
|
+
|
|
241
|
+
// Use in parent workflow with type-safe results
|
|
242
|
+
const parentWorkflow = new Workflow({ name: "parent-workflow" })
|
|
243
|
+
.step(nestedWorkflow)
|
|
244
|
+
.then(async ({ context }) => {
|
|
245
|
+
const result = context.getStepResult("nested-workflow");
|
|
246
|
+
// TypeScript knows the structure of result
|
|
247
|
+
console.log(result.total); // number
|
|
248
|
+
console.log(result.items); // Array<{ id: string, value: number }>
|
|
249
|
+
return { success: true };
|
|
250
|
+
})
|
|
251
|
+
.commit();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Best Practices
|
|
255
|
+
|
|
256
|
+
1. **Modularity**: Use nested workflows to encapsulate related steps and create reusable workflow components.
|
|
257
|
+
2. **Naming**: Give nested workflows descriptive names as they will be used as step IDs in the parent workflow.
|
|
258
|
+
3. **Error Handling**: Nested workflows propagate their errors to the parent workflow, so handle errors appropriately.
|
|
259
|
+
4. **State Management**: Each nested workflow maintains its own state but can access the parent workflow's context.
|
|
260
|
+
5. **Suspension**: When using suspension in nested workflows, consider the entire workflow's state and handle resumption appropriately.
|
|
261
|
+
|
|
262
|
+
## Example
|
|
263
|
+
|
|
264
|
+
Here's a complete example showing various features of nested workflows:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const workflowA = new Workflow({
|
|
268
|
+
name: "workflow-a",
|
|
269
|
+
result: {
|
|
270
|
+
schema: z.object({
|
|
271
|
+
activities: z.string(),
|
|
272
|
+
}),
|
|
273
|
+
mapping: {
|
|
274
|
+
activities: {
|
|
275
|
+
step: planActivities,
|
|
276
|
+
path: "activities",
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
.step(fetchWeather)
|
|
282
|
+
.then(planActivities)
|
|
283
|
+
.commit();
|
|
284
|
+
|
|
285
|
+
const workflowB = new Workflow({
|
|
286
|
+
name: "workflow-b",
|
|
287
|
+
result: {
|
|
288
|
+
schema: z.object({
|
|
289
|
+
activities: z.string(),
|
|
290
|
+
}),
|
|
291
|
+
mapping: {
|
|
292
|
+
activities: {
|
|
293
|
+
step: planActivities,
|
|
294
|
+
path: "activities",
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
})
|
|
299
|
+
.step(fetchWeather)
|
|
300
|
+
.then(planActivities)
|
|
301
|
+
.commit();
|
|
302
|
+
|
|
303
|
+
const weatherWorkflow = new Workflow({
|
|
304
|
+
name: "weather-workflow",
|
|
305
|
+
triggerSchema: z.object({
|
|
306
|
+
cityA: z.string().describe("The city to get the weather for"),
|
|
307
|
+
cityB: z.string().describe("The city to get the weather for"),
|
|
308
|
+
}),
|
|
309
|
+
result: {
|
|
310
|
+
schema: z.object({
|
|
311
|
+
activitiesA: z.string(),
|
|
312
|
+
activitiesB: z.string(),
|
|
313
|
+
}),
|
|
314
|
+
mapping: {
|
|
315
|
+
activitiesA: {
|
|
316
|
+
step: workflowA,
|
|
317
|
+
path: "result.activities",
|
|
318
|
+
},
|
|
319
|
+
activitiesB: {
|
|
320
|
+
step: workflowB,
|
|
321
|
+
path: "result.activities",
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
})
|
|
326
|
+
.step(workflowA, {
|
|
327
|
+
variables: {
|
|
328
|
+
city: {
|
|
329
|
+
step: "trigger",
|
|
330
|
+
path: "cityA",
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
.step(workflowB, {
|
|
335
|
+
variables: {
|
|
336
|
+
city: {
|
|
337
|
+
step: "trigger",
|
|
338
|
+
path: "cityB",
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
weatherWorkflow.commit();
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
In this example:
|
|
347
|
+
|
|
348
|
+
1. We define schemas for type safety across all workflows
|
|
349
|
+
2. Each step has proper input and output schemas
|
|
350
|
+
3. The nested workflows have their own trigger schemas and result mappings
|
|
351
|
+
4. Data is passed through using variables syntax in the `.step()` calls
|
|
352
|
+
5. The main workflow combines data from both nested workflows
|
|
@@ -162,7 +162,7 @@ You can:
|
|
|
162
162
|
|
|
163
163
|
## More Resources
|
|
164
164
|
|
|
165
|
-
- The [Workflow Guide](../guides/
|
|
165
|
+
- The [Workflow Guide](../guides/ai-recruiter.mdx) in the Guides section is a tutorial that covers the main concepts.
|
|
166
166
|
- [Sequential Steps workflow example](../../examples/workflows/sequential-steps.mdx)
|
|
167
167
|
- [Parallel Steps workflow example](../../examples/workflows/parallel-steps.mdx)
|
|
168
168
|
- [Branching Paths workflow example](../../examples/workflows/branching-paths.mdx)
|
|
@@ -67,36 +67,36 @@ Here's an example of a workflow with multiple steps that can suspend:
|
|
|
67
67
|
```typescript
|
|
68
68
|
// Define steps with suspend capability
|
|
69
69
|
const promptAgentStep = new Step({
|
|
70
|
-
id:
|
|
70
|
+
id: "promptAgent",
|
|
71
71
|
execute: async ({ context, suspend }) => {
|
|
72
72
|
// Some condition that determines if we need to suspend
|
|
73
73
|
if (needHumanInput) {
|
|
74
74
|
// Optionally pass payload data that will be stored with suspended state
|
|
75
|
-
await suspend({ requestReason:
|
|
75
|
+
await suspend({ requestReason: "Need human input for prompt" });
|
|
76
76
|
// Code after suspend() will execute when the step is resumed
|
|
77
77
|
return { modelOutput: context.userInput };
|
|
78
78
|
}
|
|
79
|
-
return { modelOutput:
|
|
79
|
+
return { modelOutput: "AI generated output" };
|
|
80
80
|
},
|
|
81
81
|
outputSchema: z.object({ modelOutput: z.string() }),
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
const improveResponseStep = new Step({
|
|
85
|
-
id:
|
|
85
|
+
id: "improveResponse",
|
|
86
86
|
execute: async ({ context, suspend }) => {
|
|
87
87
|
// Another condition for suspension
|
|
88
88
|
if (needFurtherRefinement) {
|
|
89
89
|
await suspend();
|
|
90
90
|
return { improvedOutput: context.refinedOutput };
|
|
91
91
|
}
|
|
92
|
-
return { improvedOutput:
|
|
92
|
+
return { improvedOutput: "Improved output" };
|
|
93
93
|
},
|
|
94
94
|
outputSchema: z.object({ improvedOutput: z.string() }),
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// Build the workflow
|
|
98
98
|
const workflow = new Workflow({
|
|
99
|
-
name:
|
|
99
|
+
name: "multi-suspend-workflow",
|
|
100
100
|
triggerSchema: z.object({ input: z.string() }),
|
|
101
101
|
});
|
|
102
102
|
|
|
@@ -108,50 +108,53 @@ workflow
|
|
|
108
108
|
.then(evaluateImproved)
|
|
109
109
|
.commit();
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
// Register the workflow with Mastra
|
|
112
|
+
export const mastra = new Mastra({
|
|
113
|
+
workflows: { workflow },
|
|
114
|
+
});
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
### Starting and Resuming the Workflow
|
|
118
118
|
|
|
119
119
|
```typescript
|
|
120
120
|
// Get the workflow and create a run
|
|
121
|
-
const wf = mastra.getWorkflow(
|
|
121
|
+
const wf = mastra.getWorkflow("multi-suspend-workflow");
|
|
122
122
|
const run = wf.createRun();
|
|
123
123
|
|
|
124
124
|
// Start the workflow
|
|
125
|
-
const initialResult = await run.start({
|
|
125
|
+
const initialResult = await run.start({
|
|
126
|
+
triggerData: { input: "initial input" },
|
|
127
|
+
});
|
|
126
128
|
|
|
127
|
-
let promptAgentStepResult = initialResult.activePaths.get(
|
|
129
|
+
let promptAgentStepResult = initialResult.activePaths.get("promptAgent");
|
|
128
130
|
let promptAgentResumeResult = undefined;
|
|
129
131
|
|
|
130
132
|
// Check if a step is suspended
|
|
131
|
-
if (promptAgentStepResult?.status ===
|
|
132
|
-
console.log(
|
|
133
|
+
if (promptAgentStepResult?.status === "suspended") {
|
|
134
|
+
console.log("Workflow suspended at promptAgent step");
|
|
133
135
|
|
|
134
136
|
// Resume the workflow with new context
|
|
135
137
|
const resumeResult = await run.resume({
|
|
136
|
-
stepId:
|
|
137
|
-
context: { userInput:
|
|
138
|
+
stepId: "promptAgent",
|
|
139
|
+
context: { userInput: "Human provided input" },
|
|
138
140
|
});
|
|
139
141
|
|
|
140
142
|
promptAgentResumeResult = resumeResult;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
const improveResponseStepResult =
|
|
145
|
+
const improveResponseStepResult =
|
|
146
|
+
promptAgentResumeResult?.activePaths.get("improveResponse");
|
|
144
147
|
|
|
145
|
-
if (improveResponseStepResult?.status ===
|
|
146
|
-
console.log(
|
|
148
|
+
if (improveResponseStepResult?.status === "suspended") {
|
|
149
|
+
console.log("Workflow suspended at improveResponse step");
|
|
147
150
|
|
|
148
151
|
// Resume again with different context
|
|
149
152
|
const finalResult = await run.resume({
|
|
150
|
-
stepId:
|
|
151
|
-
context: { refinedOutput:
|
|
153
|
+
stepId: "improveResponse",
|
|
154
|
+
context: { refinedOutput: "Human refined output" },
|
|
152
155
|
});
|
|
153
156
|
|
|
154
|
-
console.log(
|
|
157
|
+
console.log("Workflow completed:", finalResult?.results);
|
|
155
158
|
}
|
|
156
159
|
```
|
|
157
160
|
|
|
@@ -174,30 +177,30 @@ Here's how it works:
|
|
|
174
177
|
```typescript
|
|
175
178
|
// Define steps
|
|
176
179
|
const getUserInput = new Step({
|
|
177
|
-
id:
|
|
178
|
-
execute: async () => ({ userInput:
|
|
180
|
+
id: "getUserInput",
|
|
181
|
+
execute: async () => ({ userInput: "initial input" }),
|
|
179
182
|
outputSchema: z.object({ userInput: z.string() }),
|
|
180
183
|
});
|
|
181
184
|
|
|
182
185
|
const processApproval = new Step({
|
|
183
|
-
id:
|
|
186
|
+
id: "processApproval",
|
|
184
187
|
execute: async ({ context }) => {
|
|
185
188
|
// Access the event data from the context
|
|
186
189
|
const approvalData = context.inputData?.resumedEvent;
|
|
187
190
|
return {
|
|
188
191
|
approved: approvalData?.approved,
|
|
189
|
-
approvedBy: approvalData?.approverName
|
|
192
|
+
approvedBy: approvalData?.approverName,
|
|
190
193
|
};
|
|
191
194
|
},
|
|
192
195
|
outputSchema: z.object({
|
|
193
196
|
approved: z.boolean(),
|
|
194
|
-
approvedBy: z.string()
|
|
197
|
+
approvedBy: z.string(),
|
|
195
198
|
}),
|
|
196
199
|
});
|
|
197
200
|
|
|
198
201
|
// Create workflow with event definition
|
|
199
202
|
const approvalWorkflow = new Workflow({
|
|
200
|
-
name:
|
|
203
|
+
name: "approval-workflow",
|
|
201
204
|
triggerSchema: z.object({ requestId: z.string() }),
|
|
202
205
|
events: {
|
|
203
206
|
approvalReceived: {
|
|
@@ -212,8 +215,8 @@ const approvalWorkflow = new Workflow({
|
|
|
212
215
|
// Build workflow with event-based suspension
|
|
213
216
|
approvalWorkflow
|
|
214
217
|
.step(getUserInput)
|
|
215
|
-
.afterEvent(
|
|
216
|
-
.step(processApproval)
|
|
218
|
+
.afterEvent("approvalReceived") // Workflow will automatically suspend here
|
|
219
|
+
.step(processApproval) // This step runs after the event is received
|
|
217
220
|
.commit();
|
|
218
221
|
```
|
|
219
222
|
|
|
@@ -221,15 +224,15 @@ approvalWorkflow
|
|
|
221
224
|
|
|
222
225
|
```typescript
|
|
223
226
|
// Get the workflow
|
|
224
|
-
const workflow = mastra.getWorkflow(
|
|
227
|
+
const workflow = mastra.getWorkflow("approval-workflow");
|
|
225
228
|
const run = workflow.createRun();
|
|
226
229
|
|
|
227
230
|
// Start the workflow
|
|
228
231
|
const initialResult = await run.start({
|
|
229
|
-
triggerData: { requestId:
|
|
232
|
+
triggerData: { requestId: "request-123" },
|
|
230
233
|
});
|
|
231
234
|
|
|
232
|
-
console.log(
|
|
235
|
+
console.log("Workflow started, waiting for approval event");
|
|
233
236
|
console.log(initialResult.results);
|
|
234
237
|
// Output will show the workflow is suspended at the event step:
|
|
235
238
|
// {
|
|
@@ -238,12 +241,12 @@ console.log(initialResult.results);
|
|
|
238
241
|
// }
|
|
239
242
|
|
|
240
243
|
// Later, when the approval event occurs:
|
|
241
|
-
const resumeResult = await run.resumeWithEvent(
|
|
244
|
+
const resumeResult = await run.resumeWithEvent("approvalReceived", {
|
|
242
245
|
approved: true,
|
|
243
|
-
approverName:
|
|
246
|
+
approverName: "Jane Doe",
|
|
244
247
|
});
|
|
245
248
|
|
|
246
|
-
console.log(
|
|
249
|
+
console.log("Workflow resumed with event data:", resumeResult.results);
|
|
247
250
|
// Output will show the completed workflow:
|
|
248
251
|
// {
|
|
249
252
|
// getUserInput: { status: 'success', output: { userInput: 'initial input' } },
|
|
@@ -277,8 +280,8 @@ When a workflow is suspended using `await suspend()`, Mastra automatically persi
|
|
|
277
280
|
By default, Mastra uses LibSQL as its storage engine:
|
|
278
281
|
|
|
279
282
|
```typescript
|
|
280
|
-
import { Mastra } from
|
|
281
|
-
import { DefaultStorage } from
|
|
283
|
+
import { Mastra } from "@mastra/core/mastra";
|
|
284
|
+
import { DefaultStorage } from "@mastra/core/storage/libsql";
|
|
282
285
|
|
|
283
286
|
const mastra = new Mastra({
|
|
284
287
|
storage: new DefaultStorage({
|
|
@@ -287,12 +290,13 @@ const mastra = new Mastra({
|
|
|
287
290
|
// For production, use a persistent URL:
|
|
288
291
|
// url: process.env.DATABASE_URL,
|
|
289
292
|
// authToken: process.env.DATABASE_AUTH_TOKEN, // Optional for authenticated connections
|
|
290
|
-
}
|
|
293
|
+
},
|
|
291
294
|
}),
|
|
292
295
|
});
|
|
293
296
|
```
|
|
294
297
|
|
|
295
298
|
The LibSQL storage can be configured in different modes:
|
|
299
|
+
|
|
296
300
|
- In-memory database (testing): `:memory:`
|
|
297
301
|
- File-based database (development): `file:storage.db`
|
|
298
302
|
- Remote database (production): URLs like `libsql://your-database.turso.io`
|
|
@@ -308,7 +312,7 @@ npm install @mastra/upstash
|
|
|
308
312
|
```
|
|
309
313
|
|
|
310
314
|
```typescript
|
|
311
|
-
import { Mastra } from
|
|
315
|
+
import { Mastra } from "@mastra/core/mastra";
|
|
312
316
|
import { UpstashStore } from "@mastra/upstash";
|
|
313
317
|
|
|
314
318
|
const mastra = new Mastra({
|
|
@@ -318,6 +322,7 @@ const mastra = new Mastra({
|
|
|
318
322
|
}),
|
|
319
323
|
});
|
|
320
324
|
```
|
|
325
|
+
|
|
321
326
|
### Storage Considerations
|
|
322
327
|
|
|
323
328
|
- All storage options support suspend and resume functionality identically
|
|
@@ -333,24 +338,22 @@ To handle suspended workflows, use the `watch` method to monitor workflow status
|
|
|
333
338
|
import { mastra } from "./index";
|
|
334
339
|
|
|
335
340
|
// Get the workflow
|
|
336
|
-
const myWorkflow = mastra.getWorkflow(
|
|
341
|
+
const myWorkflow = mastra.getWorkflow("myWorkflow");
|
|
337
342
|
const { start, watch, resume } = myWorkflow.createRun();
|
|
338
343
|
|
|
339
344
|
// Start watching the workflow before executing it
|
|
340
|
-
watch(async ({
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
351
|
-
}
|
|
345
|
+
watch(async ({ activePaths }) => {
|
|
346
|
+
const isStepTwoSuspended = activePaths.get("stepTwo")?.status === "suspended";
|
|
347
|
+
if (isStepTwoSuspended) {
|
|
348
|
+
console.log("Workflow suspended, resuming with new value");
|
|
349
|
+
|
|
350
|
+
// Resume the workflow with new context
|
|
351
|
+
await resume({
|
|
352
|
+
stepId: "stepTwo",
|
|
353
|
+
context: { secondValue: 100 },
|
|
354
|
+
});
|
|
352
355
|
}
|
|
353
|
-
})
|
|
356
|
+
});
|
|
354
357
|
|
|
355
358
|
// Start the workflow execution
|
|
356
359
|
await start({ triggerData: { inputValue: 45 } });
|
|
@@ -364,25 +367,25 @@ You can use the same watching pattern with event-based workflows:
|
|
|
364
367
|
const { start, watch, resumeWithEvent } = workflow.createRun();
|
|
365
368
|
|
|
366
369
|
// Watch for suspended event steps
|
|
367
|
-
watch(async ({
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
}
|
|
370
|
+
watch(async ({ activePaths }) => {
|
|
371
|
+
const isApprovalReceivedSuspended =
|
|
372
|
+
activePaths.get("__approvalReceived_event")?.status === "suspended";
|
|
373
|
+
if (isApprovalReceivedSuspended) {
|
|
374
|
+
console.log("Workflow waiting for approval event");
|
|
375
|
+
|
|
376
|
+
// In a real scenario, you would wait for the actual event to occur
|
|
377
|
+
// For example, this could be triggered by a webhook or user interaction
|
|
378
|
+
setTimeout(async () => {
|
|
379
|
+
await resumeWithEvent("approvalReceived", {
|
|
380
|
+
approved: true,
|
|
381
|
+
approverName: "Auto Approver",
|
|
382
|
+
});
|
|
383
|
+
}, 5000); // Simulate event after 5 seconds
|
|
381
384
|
}
|
|
382
385
|
});
|
|
383
386
|
|
|
384
387
|
// Start the workflow
|
|
385
|
-
await start({ triggerData: { requestId:
|
|
388
|
+
await start({ triggerData: { requestId: "auto-123" } });
|
|
386
389
|
```
|
|
387
390
|
|
|
388
391
|
## Further Reading
|