@mastra/mcp-docs-server 0.13.2-alpha.2 → 0.13.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%2Fclient-js.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare-d1.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloudflare.md +15 -15
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-vercel.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fdynamodb.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fmongodb.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +26 -26
- package/.docs/organized/changelogs/%40mastra%2Fschema-compat.md +6 -0
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +7 -7
- package/.docs/organized/changelogs/%40mastra%2Fvectorize.md +8 -8
- package/.docs/organized/changelogs/%40mastra%2Fvoice-cloudflare.md +8 -8
- package/.docs/organized/changelogs/create-mastra.md +7 -7
- package/.docs/organized/changelogs/mastra.md +10 -10
- package/.docs/raw/course/01-first-agent/04-project-structure.md +8 -3
- package/.docs/raw/course/01-first-agent/07-creating-your-agent.md +5 -3
- package/.docs/raw/course/01-first-agent/08-exporting-your-agent.md +21 -6
- package/.docs/raw/course/01-first-agent/11-creating-transactions-tool.md +5 -3
- package/.docs/raw/course/01-first-agent/12-connecting-tool-to-agent.md +2 -2
- package/.docs/raw/course/04-workflows/01-introduction-to-workflows.md +44 -0
- package/.docs/raw/course/04-workflows/02-understanding-steps.md +53 -0
- package/.docs/raw/course/04-workflows/03-creating-your-first-step.md +57 -0
- package/.docs/raw/course/04-workflows/04-creating-a-second-step.md +58 -0
- package/.docs/raw/course/04-workflows/05-chaining-steps-together.md +57 -0
- package/.docs/raw/course/04-workflows/06-registering-with-mastra.md +24 -0
- package/.docs/raw/course/04-workflows/07-using-playground.md +58 -0
- package/.docs/raw/course/04-workflows/08-running-workflows-programmatically.md +77 -0
- package/.docs/raw/course/04-workflows/09-adding-a-third-step.md +70 -0
- package/.docs/raw/course/04-workflows/10-updating-the-workflow.md +55 -0
- package/.docs/raw/course/04-workflows/11-creating-an-ai-agent.md +67 -0
- package/.docs/raw/course/04-workflows/12-using-agent-in-workflow.md +91 -0
- package/.docs/raw/course/04-workflows/13-creating-ai-enhanced-workflow.md +75 -0
- package/.docs/raw/course/04-workflows/14-understanding-parallel-execution.md +38 -0
- package/.docs/raw/course/04-workflows/15-creating-parallel-steps.md +115 -0
- package/.docs/raw/course/04-workflows/16-building-parallel-workflow.md +100 -0
- package/.docs/raw/course/04-workflows/17-testing-parallel-performance.md +40 -0
- package/.docs/raw/course/04-workflows/18-understanding-conditional-branching.md +61 -0
- package/.docs/raw/course/04-workflows/19-creating-conditional-steps.md +128 -0
- package/.docs/raw/course/04-workflows/20-building-conditional-workflow.md +60 -0
- package/.docs/raw/course/04-workflows/21-testing-conditional-logic.md +58 -0
- package/.docs/raw/course/04-workflows/22-conclusion.md +58 -0
- package/package.json +4 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Using Agent in Workflow
|
|
2
|
+
|
|
3
|
+
Now you'll create a workflow step that uses your AI agent to provide intelligent content analysis.
|
|
4
|
+
|
|
5
|
+
In each step, in the execute function, you have access to the `mastra` class which provides you the ability to access Agents, Tools, and even other Workflows. In this case, we use the `mastra` class to get our agent and call that agent's `generate()` function.
|
|
6
|
+
|
|
7
|
+
## Creating an AI Analysis Step
|
|
8
|
+
|
|
9
|
+
Add this step to your workflow file:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
const aiAnalysisStep = createStep({
|
|
13
|
+
id: "ai-analysis",
|
|
14
|
+
description: "AI-powered content analysis",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
content: z.string(),
|
|
17
|
+
type: z.string(),
|
|
18
|
+
wordCount: z.number(),
|
|
19
|
+
metadata: z.object({
|
|
20
|
+
readingTime: z.number(),
|
|
21
|
+
difficulty: z.enum(["easy", "medium", "hard"]),
|
|
22
|
+
processedAt: z.string(),
|
|
23
|
+
}),
|
|
24
|
+
summary: z.string(),
|
|
25
|
+
}),
|
|
26
|
+
outputSchema: z.object({
|
|
27
|
+
content: z.string(),
|
|
28
|
+
type: z.string(),
|
|
29
|
+
wordCount: z.number(),
|
|
30
|
+
metadata: z.object({
|
|
31
|
+
readingTime: z.number(),
|
|
32
|
+
difficulty: z.enum(["easy", "medium", "hard"]),
|
|
33
|
+
processedAt: z.string(),
|
|
34
|
+
}),
|
|
35
|
+
summary: z.string(),
|
|
36
|
+
aiAnalysis: z.object({
|
|
37
|
+
score: z.number(),
|
|
38
|
+
feedback: z.string(),
|
|
39
|
+
}),
|
|
40
|
+
}),
|
|
41
|
+
execute: async ({ inputData, mastra }) => {
|
|
42
|
+
const { content, type, wordCount, metadata, summary } = inputData;
|
|
43
|
+
|
|
44
|
+
// Create prompt for the AI agent
|
|
45
|
+
const prompt = `
|
|
46
|
+
Analyze this ${type} content:
|
|
47
|
+
|
|
48
|
+
Content: "${content}"
|
|
49
|
+
Word count: ${wordCount}
|
|
50
|
+
Reading time: ${metadata.readingTime} minutes
|
|
51
|
+
Difficulty: ${metadata.difficulty}
|
|
52
|
+
|
|
53
|
+
Please provide:
|
|
54
|
+
1. A quality score from 1-10
|
|
55
|
+
2. Brief feedback on strengths and areas for improvement
|
|
56
|
+
|
|
57
|
+
Format as JSON: {"score": number, "feedback": "your feedback here"}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
// Get the contentAgent from the mastra instance.
|
|
61
|
+
const contentAgent = mastra.getAgent("contentAgent");
|
|
62
|
+
const { text } = await contentAgent.generate([
|
|
63
|
+
{ role: "user", content: prompt },
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
// Parse AI response (with fallback)
|
|
67
|
+
let aiAnalysis;
|
|
68
|
+
try {
|
|
69
|
+
aiAnalysis = JSON.parse(text);
|
|
70
|
+
} catch {
|
|
71
|
+
aiAnalysis = {
|
|
72
|
+
score: 7,
|
|
73
|
+
feedback: "AI analysis completed. " + text,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`🤖 AI Score: ${aiAnalysis.score}/10`);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
content,
|
|
81
|
+
type,
|
|
82
|
+
wordCount,
|
|
83
|
+
metadata,
|
|
84
|
+
summary,
|
|
85
|
+
aiAnalysis,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Your agent-powered step is ready! Next, you'll add it to your workflow for complete AI-enhanced content processing.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Creating AI-Enhanced Workflow
|
|
2
|
+
|
|
3
|
+
Now you'll create a new workflow that includes agent analysis alongside your existing content processing steps.
|
|
4
|
+
|
|
5
|
+
## Creating the Enhanced Workflow
|
|
6
|
+
|
|
7
|
+
Add this new workflow to your file:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
export const aiContentWorkflow = createWorkflow({
|
|
11
|
+
id: "ai-content-workflow",
|
|
12
|
+
description: "AI-enhanced content processing with analysis",
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
content: z.string(),
|
|
15
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
16
|
+
}),
|
|
17
|
+
outputSchema: z.object({
|
|
18
|
+
content: z.string(),
|
|
19
|
+
type: z.string(),
|
|
20
|
+
wordCount: z.number(),
|
|
21
|
+
metadata: z.object({
|
|
22
|
+
readingTime: z.number(),
|
|
23
|
+
difficulty: z.enum(["easy", "medium", "hard"]),
|
|
24
|
+
processedAt: z.string(),
|
|
25
|
+
}),
|
|
26
|
+
summary: z.string(),
|
|
27
|
+
aiAnalysis: z.object({
|
|
28
|
+
score: z.number(),
|
|
29
|
+
feedback: z.string(),
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
32
|
+
})
|
|
33
|
+
.then(validateContentStep)
|
|
34
|
+
.then(enhanceContentStep)
|
|
35
|
+
.then(generateSummaryStep)
|
|
36
|
+
.then(aiAnalysisStep)
|
|
37
|
+
.commit();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Registering the New Workflow
|
|
41
|
+
|
|
42
|
+
Update your Mastra configuration to include both workflows and ensure the contentAgent has been added.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// In src/mastra/index.ts
|
|
46
|
+
import {
|
|
47
|
+
contentWorkflow,
|
|
48
|
+
aiContentWorkflow,
|
|
49
|
+
} from "./workflows/content-workflow";
|
|
50
|
+
import { contentAgent } from "./agents/content-agent";
|
|
51
|
+
|
|
52
|
+
export const mastra = new Mastra({
|
|
53
|
+
workflows: {
|
|
54
|
+
contentWorkflow,
|
|
55
|
+
aiContentWorkflow, // Add the AI-enhanced version
|
|
56
|
+
},
|
|
57
|
+
agents: { contentAgent },
|
|
58
|
+
// ... rest of configuration
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Testing the Agent-Enhanced Workflow
|
|
63
|
+
|
|
64
|
+
You can now access this new Workflow inside the Mastra playground. Select this new `ai-content-workflow` workflow from the Workflows tab and run a test to validate it works as expected.
|
|
65
|
+
|
|
66
|
+
## The Complete AI Pipeline
|
|
67
|
+
|
|
68
|
+
Your AI-enhanced workflow now:
|
|
69
|
+
|
|
70
|
+
1. **Validates** content and counts words
|
|
71
|
+
2. **Enhances** with metadata
|
|
72
|
+
3. **Summarizes** the content
|
|
73
|
+
4. **Analyzes** with AI for quality scoring and feedback
|
|
74
|
+
|
|
75
|
+
This creates a comprehensive, AI-powered content processing system! Next, you'll learn about parallel execution.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Understanding Parallel Execution
|
|
2
|
+
|
|
3
|
+
Learn how to run multiple workflow steps simultaneously to improve performance when steps don't depend on each other.
|
|
4
|
+
|
|
5
|
+
## When to Use Parallel Execution
|
|
6
|
+
|
|
7
|
+
Use parallel execution when you have steps that:
|
|
8
|
+
|
|
9
|
+
- **Don't depend on each other**: Can run independently
|
|
10
|
+
- **Take time**: Network requests, AI calls, or heavy computations
|
|
11
|
+
- **Process the same input**: Multiple analyses of the same data
|
|
12
|
+
|
|
13
|
+
## Example Scenario
|
|
14
|
+
|
|
15
|
+
Imagine you want to analyze content in three different ways:
|
|
16
|
+
|
|
17
|
+
1. SEO analysis
|
|
18
|
+
2. Readability analysis
|
|
19
|
+
3. Sentiment analysis
|
|
20
|
+
|
|
21
|
+
These can all run at the same time since they don't depend on each other!
|
|
22
|
+
|
|
23
|
+
## Creating Parallel Steps
|
|
24
|
+
|
|
25
|
+
The .parallel() method on a workflow executes multiple steps in parallel.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
workflow.parallel([stepOne, stepTwo]);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Performance Benefits
|
|
32
|
+
|
|
33
|
+
Running steps in parallel:
|
|
34
|
+
|
|
35
|
+
- **Faster execution**: Steps run simultaneously instead of waiting
|
|
36
|
+
- **Improved user experience**: Shorter wait times
|
|
37
|
+
|
|
38
|
+
Next, you'll create the other parallel steps and see how to combine them!
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Creating Parallel Steps
|
|
2
|
+
|
|
3
|
+
Let's create three analysis steps that can run simultaneously to analyze different aspects of content.
|
|
4
|
+
|
|
5
|
+
## Creating the Analysis Steps
|
|
6
|
+
|
|
7
|
+
Add these three steps to your workflow file:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// SEO Analysis
|
|
11
|
+
const seoAnalysisStep = createStep({
|
|
12
|
+
id: "seo-analysis",
|
|
13
|
+
description: "SEO optimization analysis",
|
|
14
|
+
inputSchema: z.object({
|
|
15
|
+
content: z.string(),
|
|
16
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
17
|
+
}),
|
|
18
|
+
outputSchema: z.object({
|
|
19
|
+
seoScore: z.number(),
|
|
20
|
+
keywords: z.array(z.string()),
|
|
21
|
+
}),
|
|
22
|
+
execute: async ({ inputData }) => {
|
|
23
|
+
console.log("🔍 Running SEO analysis...");
|
|
24
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
25
|
+
|
|
26
|
+
const words = inputData.content.toLowerCase().split(/\s+/);
|
|
27
|
+
const keywords = words.filter((word) => word.length > 4).slice(0, 3);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
seoScore: Math.floor(Math.random() * 40) + 60,
|
|
31
|
+
keywords,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Readability Analysis
|
|
37
|
+
const readabilityStep = createStep({
|
|
38
|
+
id: "readability-analysis",
|
|
39
|
+
description: "Content readability analysis",
|
|
40
|
+
inputSchema: z.object({
|
|
41
|
+
content: z.string(),
|
|
42
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
43
|
+
}),
|
|
44
|
+
outputSchema: z.object({
|
|
45
|
+
readabilityScore: z.number(),
|
|
46
|
+
gradeLevel: z.string(),
|
|
47
|
+
}),
|
|
48
|
+
execute: async ({ inputData }) => {
|
|
49
|
+
console.log("📖 Running readability analysis...");
|
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
51
|
+
|
|
52
|
+
const sentences = inputData.content.split(/[.!?]+/).length;
|
|
53
|
+
const words = inputData.content.split(/\s+/).length;
|
|
54
|
+
const avgWordsPerSentence = words / sentences;
|
|
55
|
+
|
|
56
|
+
const score = Math.max(0, 100 - avgWordsPerSentence * 3);
|
|
57
|
+
const gradeLevel = score > 80 ? "Easy" : score > 60 ? "Medium" : "Hard";
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
readabilityScore: Math.floor(score),
|
|
61
|
+
gradeLevel,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Sentiment Analysis
|
|
67
|
+
const sentimentStep = createStep({
|
|
68
|
+
id: "sentiment-analysis",
|
|
69
|
+
description: "Content sentiment analysis",
|
|
70
|
+
inputSchema: z.object({
|
|
71
|
+
content: z.string(),
|
|
72
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
73
|
+
}),
|
|
74
|
+
outputSchema: z.object({
|
|
75
|
+
sentiment: z.enum(["positive", "neutral", "negative"]),
|
|
76
|
+
confidence: z.number(),
|
|
77
|
+
}),
|
|
78
|
+
execute: async ({ inputData }) => {
|
|
79
|
+
console.log("😊 Running sentiment analysis...");
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, 700));
|
|
81
|
+
|
|
82
|
+
const content = inputData.content.toLowerCase();
|
|
83
|
+
const positiveWords = ["good", "great", "excellent", "amazing"];
|
|
84
|
+
const negativeWords = ["bad", "terrible", "awful", "horrible"];
|
|
85
|
+
|
|
86
|
+
const positive = positiveWords.filter((word) =>
|
|
87
|
+
content.includes(word),
|
|
88
|
+
).length;
|
|
89
|
+
const negative = negativeWords.filter((word) =>
|
|
90
|
+
content.includes(word),
|
|
91
|
+
).length;
|
|
92
|
+
|
|
93
|
+
let sentiment: "positive" | "neutral" | "negative" = "neutral";
|
|
94
|
+
if (positive > negative) sentiment = "positive";
|
|
95
|
+
if (negative > positive) sentiment = "negative";
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
sentiment,
|
|
99
|
+
confidence: Math.random() * 0.3 + 0.7, // 0.7-1.0
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Notice the Timing
|
|
106
|
+
|
|
107
|
+
Each step has a different simulated processing time:
|
|
108
|
+
|
|
109
|
+
- SEO: 800ms
|
|
110
|
+
- Readability: 600ms
|
|
111
|
+
- Sentiment: 700ms
|
|
112
|
+
|
|
113
|
+
When run sequentially, total time would be ~2.2 seconds. When run in parallel, total time will be ~800ms (the longest step)!
|
|
114
|
+
|
|
115
|
+
Next, you'll learn how to run these steps in parallel using the `.parallel()` method.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Building Parallel Workflow
|
|
2
|
+
|
|
3
|
+
Now you'll create a workflow that runs your analysis steps in parallel for maximum performance.
|
|
4
|
+
|
|
5
|
+
## Creating the Parallel Workflow
|
|
6
|
+
|
|
7
|
+
Add this workflow to your file:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
export const parallelAnalysisWorkflow = createWorkflow({
|
|
11
|
+
id: "parallel-analysis-workflow",
|
|
12
|
+
description: "Run multiple content analyses in parallel",
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
content: z.string(),
|
|
15
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
16
|
+
}),
|
|
17
|
+
outputSchema: z.object({
|
|
18
|
+
results: z.object({
|
|
19
|
+
seo: z.object({
|
|
20
|
+
seoScore: z.number(),
|
|
21
|
+
keywords: z.array(z.string()),
|
|
22
|
+
}),
|
|
23
|
+
readability: z.object({
|
|
24
|
+
readabilityScore: z.number(),
|
|
25
|
+
gradeLevel: z.string(),
|
|
26
|
+
}),
|
|
27
|
+
sentiment: z.object({
|
|
28
|
+
sentiment: z.enum(["positive", "neutral", "negative"]),
|
|
29
|
+
confidence: z.number(),
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
32
|
+
}),
|
|
33
|
+
})
|
|
34
|
+
.parallel([seoAnalysisStep, readabilityStep, sentimentStep])
|
|
35
|
+
.then(
|
|
36
|
+
createStep({
|
|
37
|
+
id: "combine-results",
|
|
38
|
+
description: "Combines parallel analysis results",
|
|
39
|
+
inputSchema: z.object({
|
|
40
|
+
"seo-analysis": z.object({
|
|
41
|
+
seoScore: z.number(),
|
|
42
|
+
keywords: z.array(z.string()),
|
|
43
|
+
}),
|
|
44
|
+
"readability-analysis": z.object({
|
|
45
|
+
readabilityScore: z.number(),
|
|
46
|
+
gradeLevel: z.string(),
|
|
47
|
+
}),
|
|
48
|
+
"sentiment-analysis": z.object({
|
|
49
|
+
sentiment: z.enum(["positive", "neutral", "negative"]),
|
|
50
|
+
confidence: z.number(),
|
|
51
|
+
}),
|
|
52
|
+
}),
|
|
53
|
+
outputSchema: z.object({
|
|
54
|
+
results: z.object({
|
|
55
|
+
seo: z.object({
|
|
56
|
+
seoScore: z.number(),
|
|
57
|
+
keywords: z.array(z.string()),
|
|
58
|
+
}),
|
|
59
|
+
readability: z.object({
|
|
60
|
+
readabilityScore: z.number(),
|
|
61
|
+
gradeLevel: z.string(),
|
|
62
|
+
}),
|
|
63
|
+
sentiment: z.object({
|
|
64
|
+
sentiment: z.enum(["positive", "neutral", "negative"]),
|
|
65
|
+
confidence: z.number(),
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
execute: async ({ inputData }) => {
|
|
70
|
+
console.log("🔄 Combining parallel results...");
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
results: {
|
|
74
|
+
seo: inputData["seo-analysis"],
|
|
75
|
+
readability: inputData["readability-analysis"],
|
|
76
|
+
sentiment: inputData["sentiment-analysis"],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
)
|
|
82
|
+
.commit();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Understanding Parallel Data Flow
|
|
86
|
+
|
|
87
|
+
When steps run in parallel:
|
|
88
|
+
|
|
89
|
+
1. Each step receives the same input data
|
|
90
|
+
2. Steps execute simultaneously
|
|
91
|
+
3. Results are collected into an object with step IDs as keys
|
|
92
|
+
4. The next step receives all parallel results
|
|
93
|
+
|
|
94
|
+
## Key Points
|
|
95
|
+
|
|
96
|
+
- **`.parallel([step1, step2, step3])`**: Runs all steps simultaneously
|
|
97
|
+
- **Result object keys**: Use the step IDs (e.g., "seo-analysis")
|
|
98
|
+
- **Combine step**: Processes all parallel results together
|
|
99
|
+
|
|
100
|
+
Next, you'll test this parallel workflow and see the performance improvement!
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Testing Parallel Workflow
|
|
2
|
+
|
|
3
|
+
Let's test your parallel workflow.
|
|
4
|
+
|
|
5
|
+
## Registering the New Workflow
|
|
6
|
+
|
|
7
|
+
Update your Mastra configuration to include your new workflow workflows:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// In src/mastra/index.ts
|
|
11
|
+
import {
|
|
12
|
+
contentWorkflow,
|
|
13
|
+
aiContentWorkflow,
|
|
14
|
+
parallelAnalysisWorkflow,
|
|
15
|
+
} from "./workflows/content-workflow";
|
|
16
|
+
|
|
17
|
+
export const mastra = new Mastra({
|
|
18
|
+
workflows: {
|
|
19
|
+
contentWorkflow,
|
|
20
|
+
aiContentWorkflow,
|
|
21
|
+
parallelAnalysisWorkflow, // Add the parallel workflow
|
|
22
|
+
},
|
|
23
|
+
// ... rest of configuration
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Testing the Parallel Workflow
|
|
28
|
+
|
|
29
|
+
You can now test this new workflow in the Playground. You will notice that it processes the three analysis steps in parallel speeding up execution time.
|
|
30
|
+
|
|
31
|
+
## When to Use Parallel Execution
|
|
32
|
+
|
|
33
|
+
Use parallel execution when:
|
|
34
|
+
|
|
35
|
+
- Steps don't depend on each other's outputs
|
|
36
|
+
- Steps involve I/O operations (API calls, database queries)
|
|
37
|
+
- You want to maximize performance
|
|
38
|
+
- Steps process the same input data
|
|
39
|
+
|
|
40
|
+
Register your parallel workflow with Mastra to use it in the playground! Next, you'll learn about conditional branching.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Understanding Conditional Branching
|
|
2
|
+
|
|
3
|
+
Learn how to create workflows that take different paths based on data conditions, making your workflows more intelligent and adaptive.
|
|
4
|
+
|
|
5
|
+
## What is Conditional Branching?
|
|
6
|
+
|
|
7
|
+
Conditional branching allows workflows to:
|
|
8
|
+
|
|
9
|
+
- **Make decisions**: Choose different processing paths based on data
|
|
10
|
+
- **Handle variations**: Process different content types differently
|
|
11
|
+
- **Optimize performance**: Skip unnecessary steps for certain inputs
|
|
12
|
+
- **Customize behavior**: Provide different experiences based on conditions
|
|
13
|
+
|
|
14
|
+
## Real-World Example
|
|
15
|
+
|
|
16
|
+
Imagine a content processing workflow that:
|
|
17
|
+
|
|
18
|
+
- **Short content** (< 50 words): Gets quick processing
|
|
19
|
+
- **Medium content** (50-200 words): Gets standard processing
|
|
20
|
+
- **Long content** (> 200 words): Gets detailed processing with extra analysis
|
|
21
|
+
|
|
22
|
+
## Basic Branching Syntax
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
.branch([
|
|
26
|
+
[condition1, step1],
|
|
27
|
+
[condition2, step2],
|
|
28
|
+
[condition3, step3]
|
|
29
|
+
])
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Where:
|
|
33
|
+
|
|
34
|
+
- **condition**: An async function that returns `true` or `false`
|
|
35
|
+
- **step**: The step to execute if the condition is `true`
|
|
36
|
+
|
|
37
|
+
## Condition Functions
|
|
38
|
+
|
|
39
|
+
Conditions are functions that examine the input data:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Example condition function
|
|
43
|
+
async ({ inputData }) => {
|
|
44
|
+
return inputData.wordCount < 50;
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Multiple Paths
|
|
49
|
+
|
|
50
|
+
- If multiple conditions are `true`, **all matching steps run in parallel**
|
|
51
|
+
- If no conditions are `true`, the workflow continues without executing any branch steps
|
|
52
|
+
- Conditions are evaluated in order, but matching steps run simultaneously
|
|
53
|
+
|
|
54
|
+
## Benefits
|
|
55
|
+
|
|
56
|
+
- **Smart routing**: Send data down the most appropriate path
|
|
57
|
+
- **Performance**: Skip expensive operations when not needed
|
|
58
|
+
- **Flexibility**: Handle different scenarios in one workflow
|
|
59
|
+
- **Maintainability**: Clear logic for different processing paths
|
|
60
|
+
|
|
61
|
+
Next, you'll create a workflow with conditional branches!
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Creating Conditional Steps
|
|
2
|
+
|
|
3
|
+
Let's create two processing steps for different types of content: one for short and simple content, and one for everything else.
|
|
4
|
+
|
|
5
|
+
## Assessment Step
|
|
6
|
+
|
|
7
|
+
First, create a step that analyzes content to determine which path to take:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const assessContentStep = createStep({
|
|
11
|
+
id: "assess-content",
|
|
12
|
+
description: "Assesses content to determine processing path",
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
content: z.string(),
|
|
15
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
16
|
+
}),
|
|
17
|
+
outputSchema: z.object({
|
|
18
|
+
content: z.string(),
|
|
19
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
20
|
+
wordCount: z.number(),
|
|
21
|
+
complexity: z.enum(["simple", "moderate", "complex"]),
|
|
22
|
+
category: z.enum(["short", "medium", "long"]),
|
|
23
|
+
}),
|
|
24
|
+
execute: async ({ inputData }) => {
|
|
25
|
+
const { content, type } = inputData;
|
|
26
|
+
const words = content.trim().split(/\s+/);
|
|
27
|
+
const wordCount = words.length;
|
|
28
|
+
|
|
29
|
+
// Determine category by length
|
|
30
|
+
let category: "short" | "medium" | "long" = "short";
|
|
31
|
+
if (wordCount >= 50) category = "medium";
|
|
32
|
+
if (wordCount >= 200) category = "long";
|
|
33
|
+
|
|
34
|
+
// Determine complexity by average word length
|
|
35
|
+
const avgWordLength =
|
|
36
|
+
words.reduce((sum, word) => sum + word.length, 0) / wordCount;
|
|
37
|
+
let complexity: "simple" | "moderate" | "complex" = "simple";
|
|
38
|
+
if (avgWordLength > 5) complexity = "moderate";
|
|
39
|
+
if (avgWordLength > 7) complexity = "complex";
|
|
40
|
+
|
|
41
|
+
console.log(`📋 Assessment: ${category} content, ${complexity} complexity`);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
content,
|
|
45
|
+
type,
|
|
46
|
+
wordCount,
|
|
47
|
+
complexity,
|
|
48
|
+
category,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Processing Step
|
|
55
|
+
|
|
56
|
+
For short, simple content:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const quickProcessingStep = createStep({
|
|
60
|
+
id: "quick-processing",
|
|
61
|
+
description: "Quick processing for short and simple content",
|
|
62
|
+
inputSchema: z.object({
|
|
63
|
+
content: z.string(),
|
|
64
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
65
|
+
wordCount: z.number(),
|
|
66
|
+
complexity: z.enum(["simple", "moderate", "complex"]),
|
|
67
|
+
category: z.enum(["short", "medium", "long"]),
|
|
68
|
+
}),
|
|
69
|
+
outputSchema: z.object({
|
|
70
|
+
processedContent: z.string(),
|
|
71
|
+
processingType: z.string(),
|
|
72
|
+
recommendations: z.array(z.string()),
|
|
73
|
+
}),
|
|
74
|
+
execute: async ({ inputData }) => {
|
|
75
|
+
console.log("⚡ Quick processing for short and simple content...");
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
processedContent: inputData.content,
|
|
79
|
+
processingType: "quick",
|
|
80
|
+
recommendations: [
|
|
81
|
+
"Content is concise",
|
|
82
|
+
"Consider expanding for more detail",
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## General Processing Step
|
|
90
|
+
|
|
91
|
+
For all other content (not short and simple):
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const generalProcessingStep = createStep({
|
|
95
|
+
id: "general-processing",
|
|
96
|
+
description: "General processing for all other content",
|
|
97
|
+
inputSchema: z.object({
|
|
98
|
+
content: z.string(),
|
|
99
|
+
type: z.enum(["article", "blog", "social"]).default("article"),
|
|
100
|
+
wordCount: z.number(),
|
|
101
|
+
complexity: z.enum(["simple", "moderate", "complex"]),
|
|
102
|
+
category: z.enum(["short", "medium", "long"]),
|
|
103
|
+
}),
|
|
104
|
+
outputSchema: z.object({
|
|
105
|
+
processedContent: z.string(),
|
|
106
|
+
processingType: z.string(),
|
|
107
|
+
recommendations: z.array(z.string()),
|
|
108
|
+
}),
|
|
109
|
+
execute: async ({ inputData }) => {
|
|
110
|
+
console.log("📝 General processing for non-short/simple content...");
|
|
111
|
+
|
|
112
|
+
// Simulate more involved processing
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
processedContent: inputData.content,
|
|
117
|
+
processingType: "general",
|
|
118
|
+
recommendations: [
|
|
119
|
+
"Consider simplifying content",
|
|
120
|
+
"Break up long paragraphs",
|
|
121
|
+
"Add examples or explanations if needed",
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
These two steps will be used in different branches based on the content assessment. Next, you'll create the conditional workflow!
|