mirror of
https://github.com/vercel-labs/json-render.git
synced 2026-10-21 06:30:33 +08:00
* ai sdk * chat * move more to lib * fixes * refactor * fixes * streamdown * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * data-spec * fixes * sortable * github * tools * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * tables * fixes * fixes * rename to chat * fixes docs * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fix lint * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes * fixes
127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
import { tool } from "ai";
|
|
import { z } from "zod";
|
|
|
|
/**
|
|
* Get current weather and 7-day forecast for a city using Open-Meteo API.
|
|
* Free, no API key required.
|
|
* https://open-meteo.com/
|
|
*/
|
|
export const getWeather = tool({
|
|
description:
|
|
"Get current weather conditions and a 7-day forecast for a given city. Returns temperature, humidity, wind speed, weather conditions, and daily forecasts.",
|
|
inputSchema: z.object({
|
|
city: z
|
|
.string()
|
|
.describe("City name (e.g., 'New York', 'London', 'Tokyo')"),
|
|
}),
|
|
execute: async ({ city }) => {
|
|
// Step 1: Geocode the city name to coordinates
|
|
const geocodeUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1&language=en&format=json`;
|
|
const geocodeRes = await fetch(geocodeUrl);
|
|
|
|
if (!geocodeRes.ok) {
|
|
return { error: `Failed to geocode city: ${city}` };
|
|
}
|
|
|
|
const geocodeData = (await geocodeRes.json()) as {
|
|
results?: Array<{
|
|
name: string;
|
|
country: string;
|
|
latitude: number;
|
|
longitude: number;
|
|
timezone: string;
|
|
}>;
|
|
};
|
|
|
|
if (!geocodeData.results || geocodeData.results.length === 0) {
|
|
return { error: `City not found: ${city}` };
|
|
}
|
|
|
|
const location = geocodeData.results[0]!;
|
|
|
|
// Step 2: Get weather data
|
|
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&timezone=${encodeURIComponent(location.timezone)}&forecast_days=7`;
|
|
|
|
const weatherRes = await fetch(weatherUrl);
|
|
|
|
if (!weatherRes.ok) {
|
|
return { error: "Failed to fetch weather data" };
|
|
}
|
|
|
|
const weather = (await weatherRes.json()) as {
|
|
current: {
|
|
temperature_2m: number;
|
|
relative_humidity_2m: number;
|
|
apparent_temperature: number;
|
|
weather_code: number;
|
|
wind_speed_10m: number;
|
|
};
|
|
daily: {
|
|
time: string[];
|
|
weather_code: number[];
|
|
temperature_2m_max: number[];
|
|
temperature_2m_min: number[];
|
|
precipitation_sum: number[];
|
|
};
|
|
};
|
|
|
|
const weatherDescription = describeWeatherCode(
|
|
weather.current.weather_code,
|
|
);
|
|
|
|
const forecast = weather.daily.time.map((date, i) => ({
|
|
date,
|
|
day: new Date(date + "T12:00:00").toLocaleDateString("en-US", {
|
|
weekday: "short",
|
|
}),
|
|
high: Math.round(weather.daily.temperature_2m_max[i]!),
|
|
low: Math.round(weather.daily.temperature_2m_min[i]!),
|
|
condition: describeWeatherCode(weather.daily.weather_code[i]!),
|
|
precipitation: weather.daily.precipitation_sum[i]!,
|
|
}));
|
|
|
|
return {
|
|
city: location.name,
|
|
country: location.country,
|
|
current: {
|
|
temperature: Math.round(weather.current.temperature_2m),
|
|
feelsLike: Math.round(weather.current.apparent_temperature),
|
|
humidity: weather.current.relative_humidity_2m,
|
|
windSpeed: Math.round(weather.current.wind_speed_10m),
|
|
condition: weatherDescription,
|
|
},
|
|
forecast,
|
|
};
|
|
},
|
|
});
|
|
|
|
function describeWeatherCode(code: number): string {
|
|
const descriptions: Record<number, string> = {
|
|
0: "Clear sky",
|
|
1: "Mainly clear",
|
|
2: "Partly cloudy",
|
|
3: "Overcast",
|
|
45: "Foggy",
|
|
48: "Depositing rime fog",
|
|
51: "Light drizzle",
|
|
53: "Moderate drizzle",
|
|
55: "Dense drizzle",
|
|
61: "Slight rain",
|
|
63: "Moderate rain",
|
|
65: "Heavy rain",
|
|
71: "Slight snow",
|
|
73: "Moderate snow",
|
|
75: "Heavy snow",
|
|
77: "Snow grains",
|
|
80: "Slight rain showers",
|
|
81: "Moderate rain showers",
|
|
82: "Violent rain showers",
|
|
85: "Slight snow showers",
|
|
86: "Heavy snow showers",
|
|
95: "Thunderstorm",
|
|
96: "Thunderstorm with slight hail",
|
|
99: "Thunderstorm with heavy hail",
|
|
};
|
|
return descriptions[code] ?? "Unknown";
|
|
}
|