|
| 1 | +import { createTool } from '@mastra/core/tools'; |
| 2 | +import { z } from 'zod'; |
| 3 | + |
| 4 | +interface GeocodingResponse { |
| 5 | + results: { |
| 6 | + latitude: number; |
| 7 | + longitude: number; |
| 8 | + name: string; |
| 9 | + }[]; |
| 10 | +} |
| 11 | +interface WeatherResponse { |
| 12 | + current: { |
| 13 | + time: string; |
| 14 | + temperature_2m: number; |
| 15 | + apparent_temperature: number; |
| 16 | + relative_humidity_2m: number; |
| 17 | + wind_speed_10m: number; |
| 18 | + wind_gusts_10m: number; |
| 19 | + weather_code: number; |
| 20 | + }; |
| 21 | +} |
| 22 | + |
| 23 | +export const weatherTool = createTool({ |
| 24 | + id: 'get-weather', |
| 25 | + description: 'Get current weather for a location', |
| 26 | + inputSchema: z.object({ |
| 27 | + location: z.string().describe('City name'), |
| 28 | + }), |
| 29 | + outputSchema: z.object({ |
| 30 | + temperature: z.number(), |
| 31 | + feelsLike: z.number(), |
| 32 | + humidity: z.number(), |
| 33 | + windSpeed: z.number(), |
| 34 | + windGust: z.number(), |
| 35 | + conditions: z.string(), |
| 36 | + location: z.string(), |
| 37 | + }), |
| 38 | + execute: async (inputData) => { |
| 39 | + return await getWeather(inputData.location); |
| 40 | + }, |
| 41 | +}); |
| 42 | + |
| 43 | +const getWeather = async (location: string) => { |
| 44 | + const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`; |
| 45 | + const geocodingResponse = await fetch(geocodingUrl); |
| 46 | + const geocodingData = (await geocodingResponse.json()) as GeocodingResponse; |
| 47 | + |
| 48 | + if (!geocodingData.results?.[0]) { |
| 49 | + throw new Error(`Location '${location}' not found`); |
| 50 | + } |
| 51 | + |
| 52 | + const { latitude, longitude, name } = geocodingData.results[0]; |
| 53 | + |
| 54 | + const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`; |
| 55 | + |
| 56 | + const response = await fetch(weatherUrl); |
| 57 | + const data = (await response.json()) as WeatherResponse; |
| 58 | + |
| 59 | + return { |
| 60 | + temperature: data.current.temperature_2m, |
| 61 | + feelsLike: data.current.apparent_temperature, |
| 62 | + humidity: data.current.relative_humidity_2m, |
| 63 | + windSpeed: data.current.wind_speed_10m, |
| 64 | + windGust: data.current.wind_gusts_10m, |
| 65 | + conditions: getWeatherCondition(data.current.weather_code), |
| 66 | + location: name, |
| 67 | + }; |
| 68 | +}; |
| 69 | + |
| 70 | +function getWeatherCondition(code: number): string { |
| 71 | + const conditions: Record<number, string> = { |
| 72 | + 0: 'Clear sky', |
| 73 | + 1: 'Mainly clear', |
| 74 | + 2: 'Partly cloudy', |
| 75 | + 3: 'Overcast', |
| 76 | + 45: 'Foggy', |
| 77 | + 48: 'Depositing rime fog', |
| 78 | + 51: 'Light drizzle', |
| 79 | + 53: 'Moderate drizzle', |
| 80 | + 55: 'Dense drizzle', |
| 81 | + 56: 'Light freezing drizzle', |
| 82 | + 57: 'Dense freezing drizzle', |
| 83 | + 61: 'Slight rain', |
| 84 | + 63: 'Moderate rain', |
| 85 | + 65: 'Heavy rain', |
| 86 | + 66: 'Light freezing rain', |
| 87 | + 67: 'Heavy freezing rain', |
| 88 | + 71: 'Slight snow fall', |
| 89 | + 73: 'Moderate snow fall', |
| 90 | + 75: 'Heavy snow fall', |
| 91 | + 77: 'Snow grains', |
| 92 | + 80: 'Slight rain showers', |
| 93 | + 81: 'Moderate rain showers', |
| 94 | + 82: 'Violent rain showers', |
| 95 | + 85: 'Slight snow showers', |
| 96 | + 86: 'Heavy snow showers', |
| 97 | + 95: 'Thunderstorm', |
| 98 | + 96: 'Thunderstorm with slight hail', |
| 99 | + 99: 'Thunderstorm with heavy hail', |
| 100 | + }; |
| 101 | + return conditions[code] || 'Unknown'; |
| 102 | +} |
0 commit comments