-
Notifications
You must be signed in to change notification settings - Fork 5
Add Mastra using-a-tool example with Agentuity wrapper #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| --- | ||
| description: Guidelines for writing Agentuity AI Agents in TypeScript | ||
| globs: "**/src/agents/**/index.ts" | ||
| alwaysApply: true | ||
| --- | ||
|
|
||
| # AI Agent File | ||
|
|
||
| - Prefer using the `agentuity agent create` command to create a new Agent | ||
| - Prefer loading types from the node modules package `@agentuity/sdk` in the node_modules folder | ||
| - The file should export a default function | ||
| - Prefer naming the default function Agent or the name of the Agent based on the context of the Agent description | ||
| - All code should be in Typescript format | ||
| - Use the provided logger from the `AgentContext` interface such as `ctx.logger.info("my message: %s", "hello")` | ||
|
|
||
| ## Example Agent File | ||
|
|
||
| ```typescript | ||
| import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk"; | ||
|
|
||
| export default async function Agent(req: AgentRequest, resp: AgentResponse, ctx: AgentContext) { | ||
| return resp.json({"hello":"world"}); | ||
| } | ||
| ``` | ||
|
|
||
| ### AgentRequest | ||
|
|
||
| The AgentRequest interface provides a set of helper methods and public variables which can be used for working with data has been passed to the Agent. | ||
|
|
||
| ### AgentResponse | ||
|
|
||
| The AgentResponse interface provides a set of helper methods for responding with different data formats from the Agent. | ||
|
|
||
| ### AgentContext | ||
|
|
||
| The AgentContext has information specific to the incoming Agent request and a set of helper methods for accessing AI services like KeyValue storage and Vector storage. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| description: Guidelines for the Agentuity AI Configuration file | ||
| globs: "agentuity.yaml" | ||
| alwaysApply: true | ||
| --- | ||
|
|
||
| # Agentuity Configuration File | ||
|
|
||
| This file is used by agentuity to configure the AI Agent project. You should NOT suggest edits to this file. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| node_modules/ | ||
| .env | ||
| .env.local | ||
| .env.development | ||
| .env.production | ||
| .agentuity/ | ||
| dist/ | ||
| *.log | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Mastra Using a Tool - Agentuity Example | ||
|
|
||
| This example demonstrates how to wrap a Mastra agent that uses tools within the Agentuity framework. It preserves the original Mastra framework functionality while providing Agentuity SDK interfaces. | ||
|
|
||
| ## Overview | ||
|
|
||
| This agent uses the Mastra framework to create a London weather assistant that has access to historical weather data for the current year. The agent can answer questions about weather patterns, temperature records, rainfall, and other meteorological data. | ||
|
|
||
| ## Original Mastra Example | ||
|
|
||
| This example is based on the [Mastra "Using a Tool" documentation](https://mastra.ai/en/examples/agents/using-a-tool), which demonstrates how to add tools to Mastra agents. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Historical Weather Data**: Access to year-to-date weather information for London | ||
| - **Tool Integration**: Uses Mastra's `createTool` function to define the weather data tool | ||
| - **Agentuity Wrapper**: Wrapped with Agentuity SDK for seamless integration | ||
| - **Error Handling**: Proper error logging using Agentuity's context logger | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| src/ | ||
| ├── agents/ | ||
| │ └── london-weather-agent/ | ||
| │ └── index.ts # Main agent implementation | ||
| └── tools/ | ||
| └── london-weather-tool.ts # Weather data tool definition | ||
| ``` | ||
|
|
||
| ## Tool Functionality | ||
|
|
||
| The `londonWeatherTool` provides: | ||
| - Daily temperature maximums and minimums | ||
| - Precipitation/rainfall data | ||
| - Wind speed measurements | ||
| - Snowfall amounts | ||
| - Data from January 1st of current year to present | ||
|
|
||
| ## Usage Examples | ||
|
|
||
| - "How many times has it rained this year?" | ||
| - "What was the highest temperature recorded this year?" | ||
| - "Show me the weather trends for the past month" | ||
| - "What's the average rainfall so far this year?" | ||
|
|
||
| ## Setup | ||
|
|
||
| 1. Install dependencies: | ||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| 2. Start development server: | ||
| ```bash | ||
| agentuity dev | ||
| ``` | ||
|
|
||
| 3. Test the agent in the Agentuity Console | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - **@mastra/core**: Core Mastra framework for agent and tool creation | ||
| - **@ai-sdk/openai**: OpenAI integration for the language model | ||
| - **@agentuity/sdk**: Agentuity SDK for request/response handling | ||
| - **zod**: Schema validation for tool inputs/outputs | ||
|
|
||
| ## Implementation Notes | ||
|
|
||
| This example follows the Agentuity wrapper pattern: | ||
| - Preserves all original Mastra framework imports and functionality | ||
| - Uses Agentuity SDK types for request/response handling | ||
| - Implements proper error handling with context logging | ||
| - Provides a welcome function with example prompts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| name: mastra-using-a-tool | ||
| description: Mastra agent with London weather tool wrapped in Agentuity SDK | ||
| version: 0.0.1 | ||
| runtime: nodejs | ||
|
|
||
| agents: | ||
| london-weather-agent: | ||
| name: London Weather Agent | ||
| description: Provides historical weather data for London using Mastra tools | ||
| path: src/agents/london-weather-agent | ||
|
|
||
| development: | ||
| port: 3000 | ||
|
|
||
| deployment: | ||
| memory: 512 | ||
| timeout: 30 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import { serve } from "@agentuity/sdk"; | ||
|
|
||
| serve(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| { | ||
| "name": "mastra-using-a-tool", | ||
| "description": "Mastra agent with London weather tool wrapped in Agentuity SDK", | ||
| "version": "0.0.1", | ||
| "main": "index.js", | ||
| "type": "module", | ||
| "scripts": { | ||
| "build": "agentuity build", | ||
| "prestart": "agentuity bundle", | ||
| "start": "node --env-file .env .agentuity/index.js", | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| }, | ||
| "keywords": ["agentuity", "agent", "ai", "ai agent", "mastra", "weather"], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "devDependencies": { | ||
| "@types/node": "^22.13.9", | ||
| "mastra": "^0.2.9-alpha.4", | ||
| "typescript": "^5.8.2" | ||
| }, | ||
| "dependencies": { | ||
| "@agentuity/sdk": "^0.0.73", | ||
| "@ai-sdk/openai": "^1.2.1", | ||
| "@mastra/core": "^0.5.0-alpha.4", | ||
| "ai": "^4.1.53", | ||
| "zod": "^3.24.2" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk"; | ||
| import { openai } from "@ai-sdk/openai"; | ||
| import { Agent } from "@mastra/core"; | ||
| import { londonWeatherTool } from "../../tools/london-weather-tool"; | ||
|
Comment on lines
+1
to
+4
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix fatal name collision: local function shadows imported Mastra Agent.
-import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk";
-import { openai } from "@ai-sdk/openai";
-import { Agent } from "@mastra/core";
+import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk";
+import { openai } from "@ai-sdk/openai";
+import { Agent as MastraAgent } from "@mastra/core";
@@
-export default async function Agent(
+export default async function handler(
req: AgentRequest,
resp: AgentResponse,
ctx: AgentContext,
) {
@@
- const londonWeatherAgent = new Agent({
+ const londonWeatherAgent = new MastraAgent({
name: "london-weather-agent",This also resolves the Biome “noRedeclare” warning. Also applies to: 18-41 🤖 Prompt for AI Agents |
||
|
|
||
| export function welcome() { | ||
| return { | ||
| message: "I'm a London weather assistant with access to historical weather data for the current year. I can help you analyze weather patterns, trends, and specific data points.", | ||
| prompts: [ | ||
| "How many times has it rained this year?", | ||
| "What was the highest temperature recorded this year?", | ||
| "Show me the weather trends for the past month", | ||
| "What's the average rainfall so far this year?", | ||
| ], | ||
| }; | ||
| } | ||
|
|
||
| export default async function Agent( | ||
| req: AgentRequest, | ||
| resp: AgentResponse, | ||
| ctx: AgentContext, | ||
| ) { | ||
| try { | ||
| const prompt = req.text() ?? "What is the weather data for London this year?"; | ||
|
|
||
| const londonWeatherAgent = new Agent({ | ||
| name: "london-weather-agent", | ||
| description: "Provides historical information about London weather", | ||
| instructions: `You are a helpful assistant with access to historical weather data for London. | ||
|
|
||
| The data is provided for the current calendar year from January 1st up to today. When responding: | ||
| - Use the londonWeatherTool to fetch current year's weather data for London | ||
| - The data includes daily temperature maximums and minimums, rainfall, wind speed, and snowfall | ||
| - Answer the user's question using that data | ||
| - Keep responses concise, factual, and informative | ||
| - If the question cannot be answered with available data, say so clearly`, | ||
| model: openai("gpt-4o"), | ||
| tools: { londonWeatherTool }, | ||
| }); | ||
|
|
||
| const result = await londonWeatherAgent.generate(prompt); | ||
| return resp.text(result.text); | ||
| } catch (error) { | ||
| ctx.logger.error( | ||
| "Error generating response: %s", | ||
| error instanceof Error ? error.message : String(error), | ||
| ); | ||
|
|
||
| return resp.text( | ||
| "I'm sorry, I encountered an error while processing your request.", | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,34 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createTool } from "@mastra/core"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const londonWeatherTool = createTool({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "london-weather-tool", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: "Returns year-to-date historical weather data for London", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputSchema: z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| date: z.array(z.string()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temp_max: z.array(z.number()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temp_min: z.array(z.number()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rainfall: z.array(z.number()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| windspeed: z.array(z.number()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| snowfall: z.array(z.number()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const startDate = new Date().getFullYear() + "-01-01"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const endDate = new Date().toISOString().split("T")[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await fetch( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `https://archive-api.open-meteo.com/v1/archive?latitude=51.5072&longitude=-0.1276&start_date=${startDate}&end_date=${endDate}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,snowfall_sum`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { daily } = await response.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| date: daily.time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temp_max: daily.temperature_2m_max, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temp_min: daily.temperature_2m_min, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rainfall: daily.precipitation_sum, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| windspeed: daily.wind_speed_10m_max, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| snowfall: daily.snowfall_sum, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling, timeout, and response shape validation for the external API call. Current code assumes success and valid shape; a network error or schema drift will crash the tool. execute: async () => {
- const startDate = new Date().getFullYear() + "-01-01";
- const endDate = new Date().toISOString().split("T")[0];
-
- const response = await fetch(
- `https://archive-api.open-meteo.com/v1/archive?latitude=51.5072&longitude=-0.1276&start_date=${startDate}&end_date=${endDate}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,snowfall_sum`,
- );
-
- const { daily } = await response.json();
-
- return {
- date: daily.time,
- temp_max: daily.temperature_2m_max,
- temp_min: daily.temperature_2m_min,
- rainfall: daily.precipitation_sum,
- windspeed: daily.wind_speed_10m_max,
- snowfall: daily.snowfall_sum,
- };
+ const now = new Date();
+ const startDate = `${now.getFullYear()}-01-01`;
+ const endDate = now.toISOString().split("T")[0];
+
+ const controller = new AbortController();
+ const timeout = setTimeout(() => controller.abort(), 10_000);
+ try {
+ const url =
+ `https://archive-api.open-meteo.com/v1/archive` +
+ `?latitude=51.5072&longitude=-0.1276` +
+ `&start_date=${startDate}&end_date=${endDate}` +
+ `&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,snowfall_sum`;
+
+ const response = await fetch(url, {
+ signal: controller.signal,
+ headers: { accept: "application/json" },
+ });
+ if (!response.ok) {
+ throw new Error(`Open-Meteo request failed: ${response.status} ${response.statusText}`);
+ }
+ const json = await response.json();
+ const daily = json?.daily;
+ if (!daily || !Array.isArray(daily.time)) {
+ throw new Error("Unexpected response shape: missing daily time series");
+ }
+ const normalize = (arr: unknown[]) => (Array.isArray(arr) ? arr.map((v: any) => (v ?? 0)) : []);
+ return {
+ date: daily.time,
+ temp_max: normalize(daily.temperature_2m_max),
+ temp_min: normalize(daily.temperature_2m_min),
+ rainfall: normalize(daily.precipitation_sum),
+ windspeed: normalize(daily.wind_speed_10m_max),
+ snowfall: normalize(daily.snowfall_sum),
+ };
+ } finally {
+ clearTimeout(timeout);
+ }
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2022", | ||
| "lib": ["ES2022"], | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", | ||
| "allowImportingTsExtensions": true, | ||
| "noEmit": true, | ||
| "strict": true, | ||
| "noUnusedLocals": true, | ||
| "noUnusedParameters": true, | ||
| "noFallthroughCasesInSwitch": true, | ||
| "skipLibCheck": true, | ||
| "allowSyntheticDefaultImports": true, | ||
| "esModuleInterop": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "resolveJsonModule": true, | ||
| "isolatedModules": true, | ||
| "verbatimModuleSyntax": true | ||
| }, | ||
| "include": ["src/**/*", "index.ts"], | ||
| "exclude": ["node_modules", "dist"] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spell “TypeScript” correctly.
User-facing docs should use the correct casing.
📝 Committable suggestion
🤖 Prompt for AI Agents