Skip to content

Commit 4b0cc40

Browse files
authored
refactor(dojo): improve structure for mastra example for code generation (#750)
Signed-off-by: Tyler Slaton <[email protected]>
1 parent 17ac24e commit 4b0cc40

File tree

9 files changed

+483
-200
lines changed

9 files changed

+483
-200
lines changed

apps/dojo/scripts/generate-content-json.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ function parseAgentsFile(): Array<{ id: string; agentKeys: string[] }> {
2222
const returnMatch = block.match(
2323
/agents:\s*async\s*\(\)\s*=>\s*{\s*return\s*{/,
2424
);
25-
if (!returnMatch) continue;
25+
26+
// If no return match, still add the config with empty keys if it has a mapper
27+
// This handles dynamic agent discovery like Mastra
28+
if (!returnMatch) {
29+
agentConfigs.push({ id, agentKeys: [] });
30+
continue;
31+
}
2632

2733
const startIndex = returnMatch.index! + returnMatch[0].length;
2834
const returnObjectContent = extractBalancedBraces(block, startIndex);
@@ -208,7 +214,7 @@ const agentFilesMapper: Record<
208214
path.join(
209215
__dirname,
210216
integrationsFolderPath,
211-
`/mastra/typescript/examples/src/mastra/agents/weather-agent.ts`,
217+
`/mastra/typescript/examples/src/mastra/agents/agentic-chat.ts`,
212218
),
213219
],
214220
backend_tool_rendering: [
@@ -218,24 +224,29 @@ const agentFilesMapper: Record<
218224
`/mastra/typescript/examples/src/mastra/agents/backend-tool-rendering.ts`,
219225
),
220226
],
227+
human_in_the_loop: [
228+
path.join(
229+
__dirname,
230+
integrationsFolderPath,
231+
`/mastra/typescript/examples/src/mastra/agents/human-in-the-loop.ts`,
232+
),
233+
],
221234
tool_based_generative_ui: [
222235
path.join(
223236
__dirname,
224237
integrationsFolderPath,
225-
`/mastra/example/src/mastra/agents/haiku-agent.ts`,
238+
`/mastra/typescript/examples/src/mastra/agents/tool-based-generative-ui.ts`,
226239
),
227240
],
228241
}),
229242

230-
"mastra-agent-local": (agentKeys: string[]) => {
231-
return agentKeys.reduce(
232-
(acc, agentId) => ({
233-
...acc,
234-
[agentId]: [path.join(__dirname, "../src/mastra/index.ts")],
235-
}),
236-
{},
237-
);
238-
},
243+
"mastra-agent-local": () => ({
244+
agentic_chat: [path.join(__dirname, "../src/mastra/agents/agentic-chat.ts")],
245+
human_in_the_loop: [path.join(__dirname, "../src/mastra/agents/human-in-the-loop.ts")],
246+
backend_tool_rendering: [path.join(__dirname, "../src/mastra/agents/backend-tool-rendering.ts")],
247+
shared_state: [path.join(__dirname, "../src/mastra/agents/shared-state.ts")],
248+
tool_based_generative_ui: [path.join(__dirname, "../src/mastra/agents/tool-based-generative-ui.ts")],
249+
}),
239250

240251
"vercel-ai-sdk": () => ({
241252
agentic_chat: [
@@ -418,8 +429,14 @@ async function runGenerateContent() {
418429
continue;
419430
}
420431

432+
// If agentsPerFeatures is empty but we have agentFilePaths, use the keys from agentFilePaths
433+
// This handles cases like Mastra where agents are dynamically discovered
434+
const featureIds = agentsPerFeatures.length > 0
435+
? agentsPerFeatures
436+
: Object.keys(agentFilePaths);
437+
421438
// Per feature, assign all the frontend files like page.tsx as well as all agent files
422-
for (const featureId of agentsPerFeatures) {
439+
for (const featureId of featureIds) {
423440
const agentFilePathsForFeature = agentFilePaths[featureId] ?? [];
424441
// @ts-expect-error -- redundant error about indexing of a new object.
425442
result[`${agentConfig.id}::${featureId}`] = [

apps/dojo/src/files.json

Lines changed: 234 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { openai } from "@ai-sdk/openai";
2+
import { Agent } from "@mastra/core/agent";
3+
import { Memory } from "@mastra/memory";
4+
import { z } from "zod";
5+
import { weatherTool } from "../tools";
6+
import { getStorage } from "../storage";
7+
8+
export const agenticChatAgent = new Agent({
9+
name: "agentic_chat",
10+
instructions: `
11+
You are a helpful weather assistant that provides accurate weather information.
12+
13+
Your primary function is to help users get weather details for specific locations. When responding:
14+
- Always ask for a location if none is provided
15+
- If the location name isn't in English, please translate it
16+
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
17+
- Include relevant details like humidity, wind conditions, and precipitation
18+
- Keep responses concise but informative
19+
`,
20+
model: openai("gpt-4o"),
21+
tools: { get_weather: weatherTool },
22+
memory: new Memory({
23+
storage: getStorage(),
24+
options: {
25+
workingMemory: {
26+
enabled: true,
27+
schema: z.object({
28+
firstName: z.string(),
29+
}),
30+
},
31+
},
32+
}),
33+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { openai } from "@ai-sdk/openai";
2+
import { Agent } from "@mastra/core/agent";
3+
import { Memory } from "@mastra/memory";
4+
import { weatherTool } from "../tools";
5+
import { getStorage } from "../storage";
6+
7+
export const backendToolRenderingAgent = new Agent({
8+
name: "Weather Agent",
9+
instructions: `
10+
You are a helpful weather assistant that provides accurate weather information.
11+
12+
Your primary function is to help users get weather details for specific locations. When responding:
13+
- Always ask for a location if none is provided
14+
- If the location name isn't in English, please translate it
15+
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
16+
- Include relevant details like humidity, wind conditions, and precipitation
17+
- Keep responses concise but informative
18+
19+
Use the weatherTool to fetch current weather data.
20+
`,
21+
model: openai("gpt-4o-mini"),
22+
tools: { get_weather: weatherTool },
23+
memory: new Memory({
24+
storage: getStorage(),
25+
}),
26+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { openai } from "@ai-sdk/openai";
2+
import { Agent } from "@mastra/core/agent";
3+
import { Memory } from "@mastra/memory";
4+
import { getStorage } from "../storage";
5+
6+
export const humanInTheLoopAgent = new Agent({
7+
name: "Task Planning Agent",
8+
instructions: `
9+
You are a helpful task planning assistant that helps users break down tasks into actionable steps.
10+
11+
When planning tasks use tools only, without any other messages.
12+
IMPORTANT:
13+
- Use the \`generate_task_steps\` tool to display the suggested steps to the user
14+
- Do not call the \`generate_task_steps\` twice in a row, ever.
15+
- Never repeat the plan, or send a message detailing steps
16+
- If accepted, confirm the creation of the plan and the number of selected (enabled) steps only
17+
- If not accepted, ask the user for more information, DO NOT use the \`generate_task_steps\` tool again
18+
19+
When responding to user requests:
20+
- Always break down the task into clear, actionable steps
21+
- Use imperative form for each step (e.g., "Book flight", "Pack luggage", "Check passport")
22+
- Keep steps concise but descriptive
23+
- Make sure steps are in logical order
24+
`,
25+
model: openai("gpt-4o-mini"),
26+
memory: new Memory({
27+
storage: getStorage(),
28+
}),
29+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { openai } from "@ai-sdk/openai";
2+
import { Agent } from "@mastra/core/agent";
3+
import { Memory } from "@mastra/memory";
4+
import { z } from "zod";
5+
import { getStorage } from "../storage";
6+
7+
export const sharedStateAgent = new Agent({
8+
name: "shared_state",
9+
instructions: `
10+
You are a helpful assistant for creating recipes.
11+
12+
IMPORTANT:
13+
1. Create a recipe using the existing ingredients and instructions. Make sure the recipe is complete.
14+
2. For ingredients, append new ingredients to the existing ones.
15+
3. For instructions, append new steps to the existing ones.
16+
4. 'ingredients' is always an array of objects with 'icon', 'name', and 'amount' fields
17+
5. 'instructions' is always an array of strings
18+
19+
If you have just created or modified the recipe, just answer in one sentence what you did. dont describe the recipe, just say what you did. Do not mention "working memory", "memory", or "state" in your answer.
20+
`,
21+
model: openai("gpt-4o"),
22+
memory: new Memory({
23+
storage: getStorage(),
24+
options: {
25+
workingMemory: {
26+
enabled: true,
27+
schema: z.object({
28+
recipe: z.object({
29+
skill_level: z
30+
.enum(["Beginner", "Intermediate", "Advanced"])
31+
.describe("The skill level required for the recipe"),
32+
special_preferences: z
33+
.array(
34+
z.enum([
35+
"High Protein",
36+
"Low Carb",
37+
"Spicy",
38+
"Budget-Friendly",
39+
"One-Pot Meal",
40+
"Vegetarian",
41+
"Vegan",
42+
]),
43+
)
44+
.describe("A list of special preferences for the recipe"),
45+
cooking_time: z
46+
.enum(["5 min", "15 min", "30 min", "45 min", "60+ min"])
47+
.describe("The cooking time of the recipe"),
48+
ingredients: z
49+
.array(
50+
z.object({
51+
icon: z
52+
.string()
53+
.describe(
54+
"The icon emoji (not emoji code like '\\x1f35e', but the actual emoji like 🥕) of the ingredient",
55+
),
56+
name: z.string().describe("The name of the ingredient"),
57+
amount: z.string().describe("The amount of the ingredient"),
58+
}),
59+
)
60+
.describe(
61+
"Entire list of ingredients for the recipe, including the new ingredients and the ones that are already in the recipe",
62+
),
63+
instructions: z
64+
.array(z.string())
65+
.describe(
66+
"Entire list of instructions for the recipe, including the new instructions and the ones that are already there",
67+
),
68+
changes: z.string().describe("A description of the changes made to the recipe"),
69+
}),
70+
}),
71+
},
72+
},
73+
}),
74+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { openai } from "@ai-sdk/openai";
2+
import { Agent } from "@mastra/core/agent";
3+
import { createTool } from "@mastra/core";
4+
import { z } from "zod";
5+
6+
export const toolBasedGenerativeUIAgent = new Agent({
7+
name: "tool_based_generative_ui",
8+
instructions: `
9+
You are a helpful assistant for creating haikus.
10+
`,
11+
model: openai("gpt-4o"),
12+
tools: {
13+
generate_haiku: createTool({
14+
id: "generate_haiku",
15+
description:
16+
"Generate a haiku in Japanese and its English translation. Also select exactly 3 relevant images from the provided list based on the haiku's theme.",
17+
inputSchema: z.object({
18+
japanese: z
19+
.array(z.string())
20+
.describe("An array of three lines of the haiku in Japanese"),
21+
english: z
22+
.array(z.string())
23+
.describe("An array of three lines of the haiku in English"),
24+
}),
25+
outputSchema: z.string(),
26+
execute: async ({ context }) => {
27+
return "Haiku generated.";
28+
},
29+
}),
30+
},
31+
});

0 commit comments

Comments
 (0)