From 1c1368272138ea65c277115c15a82288100ae62f Mon Sep 17 00:00:00 2001 From: josh <144584931+dancer@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:28:04 +0100 Subject: [PATCH] chore: updated weather tool (#1269) --- components/weather.tsx | 146 +++++++++++++++++++++++++----------- lib/ai/tools/get-weather.ts | 63 ++++++++++++++-- 2 files changed, 159 insertions(+), 50 deletions(-) diff --git a/components/weather.tsx b/components/weather.tsx index 38ccc05..d4e7234 100644 --- a/components/weather.tsx +++ b/components/weather.tsx @@ -4,6 +4,32 @@ import cx from "classnames"; import { format, isWithinInterval } from "date-fns"; import { useEffect, useState } from "react"; +const SunIcon = ({ size = 40 }: { size?: number }) => ( + + + + + + + + + + + +); + +const MoonIcon = ({ size = 40 }: { size?: number }) => ( + + + +); + +const CloudIcon = ({ size = 24 }: { size?: number }) => ( + + + +); + type WeatherAtLocation = { latitude: number; longitude: number; @@ -12,6 +38,7 @@ type WeatherAtLocation = { timezone: string; timezone_abbreviation: string; elevation: number; + cityName?: string; current_units: { time: string; interval: string; @@ -233,12 +260,10 @@ export function Weather({ const hoursToShow = isMobile ? 5 : 6; - // Find the index of the current time or the next closest time const currentTimeIndex = weatherAtLocation.hourly.time.findIndex( (time) => new Date(time) >= new Date(weatherAtLocation.current.time) ); - // Slice the arrays to get the desired number of items const displayTimes = weatherAtLocation.hourly.time.slice( currentTimeIndex, currentTimeIndex + hoursToShow @@ -248,63 +273,96 @@ export function Weather({ currentTimeIndex + hoursToShow ); + const location = weatherAtLocation.cityName || + `${weatherAtLocation.latitude?.toFixed(1)}°, ${weatherAtLocation.longitude?.toFixed(1)}°`; + return (
-
-
-
-
- {n(weatherAtLocation.current.temperature_2m)} - {weatherAtLocation.current_units.temperature_2m} +
+ +
+
+
+ {location} +
+
+ {format(new Date(weatherAtLocation.current.time), "MMM d, h:mm a")}
-
{`H:${n(currentHigh)}° L:${n(currentLow)}°`}
-
- -
- {displayTimes.map((time, index) => ( -
-
- {format(new Date(time), "ha")} +
+
+
+ {isDay ? : }
-
-
- {n(displayTemperatures[index])} - {weatherAtLocation.hourly_units.temperature_2m} +
+ {n(weatherAtLocation.current.temperature_2m)} + + {weatherAtLocation.current_units.temperature_2m} +
- ))} + +
+
+ H: {n(currentHigh)}° +
+
+ L: {n(currentLow)}° +
+
+
+ +
+
+ Hourly Forecast +
+
+ {displayTimes.map((time, index) => { + const hourTime = new Date(time); + const isCurrentHour = hourTime.getHours() === new Date().getHours(); + + return ( +
+
+ {index === 0 ? "Now" : format(hourTime, "ha")} +
+ +
+ +
+ +
+ {n(displayTemperatures[index])}° +
+
+ ); + })} +
+
+ +
+
Sunrise: {format(new Date(weatherAtLocation.daily.sunrise[0]), "h:mm a")}
+
Sunset: {format(new Date(weatherAtLocation.daily.sunset[0]), "h:mm a")}
+
); diff --git a/lib/ai/tools/get-weather.ts b/lib/ai/tools/get-weather.ts index 221f4ef..c114833 100644 --- a/lib/ai/tools/get-weather.ts +++ b/lib/ai/tools/get-weather.ts @@ -1,18 +1,69 @@ import { tool } from "ai"; import { z } from "zod"; +async function geocodeCity(city: string): Promise<{ latitude: number; longitude: number } | null> { + try { + const response = await fetch( + `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1&language=en&format=json` + ); + + if (!response.ok) return null; + + const data = await response.json(); + + if (!data.results || data.results.length === 0) { + return null; + } + + const result = data.results[0]; + return { + latitude: result.latitude, + longitude: result.longitude, + }; + } catch { + return null; + } +} + export const getWeather = tool({ - description: "Get the current weather at a location", - inputSchema: z.object({ - latitude: z.number(), - longitude: z.number(), - }), - execute: async ({ latitude, longitude }) => { + description: "Get the current weather at a location. You can provide either coordinates or a city name.", + inputSchema: z.union([ + z.object({ + latitude: z.number(), + longitude: z.number(), + }), + z.object({ + city: z.string().describe("City name (e.g., 'San Francisco', 'New York', 'London')"), + }), + ]), + execute: async (input) => { + let latitude: number; + let longitude: number; + + if ("city" in input) { + const coords = await geocodeCity(input.city); + if (!coords) { + return { + error: `Could not find coordinates for "${input.city}". Please check the city name.`, + }; + } + latitude = coords.latitude; + longitude = coords.longitude; + } else { + latitude = input.latitude; + longitude = input.longitude; + } + const response = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto` ); const weatherData = await response.json(); + + if ("city" in input) { + weatherData.cityName = input.city; + } + return weatherData; }, });