import { Agent, createStep, createWorkflow, scaleway } from "@ai_kit/core";
import { z } from "zod";
type WeatherInput = { city: string };
type WeatherSnapshot = {
location: string;
temperature: number;
feelsLike: number;
humidity: number;
windSpeed: number;
windGust: number;
conditions: string;
};
type AdviceOutput = { text: string };
type WorkflowMeta = Record<string, unknown>;
const weatherCodeLabels: Record<number, string> = {
0: "Clear sky",
1: "Mostly clear",
2: "Partly cloudy",
3: "Overcast",
45: "Fog",
48: "Freezing fog",
51: "Light drizzle",
53: "Moderate drizzle",
55: "Dense drizzle",
56: "Light freezing drizzle",
57: "Heavy freezing drizzle",
61: "Light rain",
63: "Moderate rain",
65: "Heavy rain",
66: "Light freezing rain",
67: "Heavy freezing rain",
71: "Light snow",
73: "Moderate snow",
75: "Heavy snow",
77: "Snow grains",
80: "Light rain showers",
81: "Moderate rain showers",
82: "Violent rain showers",
85: "Light snow showers",
86: "Heavy snow showers",
95: "Thunderstorm",
96: "Thunderstorm with light hail",
99: "Thunderstorm with heavy hail",
};
const fetchWeather = createStep<WeatherInput, { forecast: WeatherSnapshot }>({
id: "fetch-weather",
description: "Fetch weather data from an external service",
inputSchema: z.object({ city: z.string().min(1) }),
outputSchema: z.object({
location: z.string(),
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
windGust: z.number(),
conditions: z.string(),
}),
handler: async ({ input, signal }) => {
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code&timezone=Europe/Paris&latitude=48.8566&longitude=2.3522`,
{ signal },
);
if (!response.ok) {
throw new Error(`Weather API error (${response.status})`);
}
const body = await response.json();
const current = body.current;
const snapshot: WeatherSnapshot = {
location: input.city,
temperature: current.temperature_2m,
feelsLike: current.apparent_temperature,
humidity: current.relative_humidity_2m,
windSpeed: current.wind_speed_10m,
windGust: current.wind_gusts_10m,
conditions:
weatherCodeLabels[current.weather_code as keyof typeof weatherCodeLabels] ??
"Unknown conditions",
};
return { forecast: snapshot };
},
});
const generateAdvice = createStep<
{ forecast: WeatherSnapshot },
AdviceOutput,
WorkflowMeta
>({
id: "generate-advice",
description: "Use an agent to craft weather advice",
handler: async ({ input, context }) => {
context.emit({
type: "forecast",
data: input.forecast,
});
const agent = new Agent({
name: "weather-advisor",
instructions:
"You are a friendly weather assistant that provides practical advice.",
model: scaleway("gpt-oss-120b"),
});
const advice = await agent.generate({
prompt: `City: ${input.forecast.location}
Feels like: ${input.forecast.feelsLike}°C
Conditions: ${input.forecast.conditions}
Humidity: ${input.forecast.humidity}%
Wind: ${input.forecast.windSpeed} km/h (gusts ${input.forecast.windGust} km/h)
Write a short message in French that summarises the weather and gives a concrete tip.`,
});
return { text: advice.text };
},
});
export const weatherAdvisorWorkflow = createWorkflow<
WeatherInput,
AdviceOutput,
WorkflowMeta
>({
id: "weather-advisor",
description: "Weather advisory workflow powered by an agent",
})
.then(fetchWeather)
.then(generateAdvice)
.commit();