",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/transitive-bullshit/agentic.git"
+ },
+ "type": "module",
+ "source": "./src/index.ts",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "default": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "tsup",
+ "dev": "tsup --watch",
+ "clean": "del dist",
+ "test": "run-s test:*",
+ "test:lint": "eslint .",
+ "test:typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@agentic/core": "workspace:*",
+ "ky": "catalog:"
+ },
+ "peerDependencies": {
+ "zod": "catalog:"
+ },
+ "devDependencies": {
+ "@agentic/tsconfig": "workspace:*"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/legacy/packages/open-meteo/readme.md b/legacy/packages/open-meteo/readme.md
new file mode 100644
index 00000000..38781f32
--- /dev/null
+++ b/legacy/packages/open-meteo/readme.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ AI agent stdlib that works with any LLM and TypeScript AI SDK.
+
+
+
+
+
+
+
+
+
+# Agentic
+
+**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.**
+
+## License
+
+MIT © [Travis Fischer](https://x.com/transitive_bs)
diff --git a/legacy/packages/open-meteo/src/index.ts b/legacy/packages/open-meteo/src/index.ts
new file mode 100644
index 00000000..e334386f
--- /dev/null
+++ b/legacy/packages/open-meteo/src/index.ts
@@ -0,0 +1,2 @@
+export * from './open-meteo'
+export * from './open-meteo-client'
diff --git a/legacy/packages/open-meteo/src/open-meteo-client.ts b/legacy/packages/open-meteo/src/open-meteo-client.ts
new file mode 100644
index 00000000..9084d247
--- /dev/null
+++ b/legacy/packages/open-meteo/src/open-meteo-client.ts
@@ -0,0 +1,144 @@
+import {
+ aiFunction,
+ AIFunctionsProvider,
+ getEnv,
+ pick,
+ sanitizeSearchParams
+} from '@agentic/core'
+import defaultKy, { type KyInstance } from 'ky'
+
+import { openmeteo } from './open-meteo'
+
+/**
+ * Agentic OpenMeteo weather client.
+ *
+ * Open-Meteo offers free weather forecast APIs for open-source developers
+ * and non-commercial use.
+ *
+ * @note The API key is optional.
+ *
+ * @see https://open-meteo.com/en/docs
+ */
+export class OpenMeteoClient extends AIFunctionsProvider {
+ protected readonly ky: KyInstance
+ protected readonly apiKey: string | undefined
+ protected readonly apiBaseUrl: string
+
+ constructor({
+ apiKey = getEnv('OPEN_METEO_API_KEY'),
+ apiBaseUrl = openmeteo.apiBaseUrl,
+ ky = defaultKy
+ }: {
+ apiKey?: string
+ apiBaseUrl?: string
+ ky?: KyInstance
+ } = {}) {
+ super()
+
+ this.apiKey = apiKey
+ this.apiBaseUrl = apiBaseUrl
+
+ this.ky = ky.extend({
+ prefixUrl: apiBaseUrl,
+ ...(apiKey
+ ? {
+ headers: {
+ Authorization: `Bearer ${apiKey}`
+ }
+ }
+ : {})
+ })
+ }
+
+ /**
+ * Gets the 7-day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide.
+ */
+ @aiFunction({
+ name: 'open_meteo_get_forecast',
+ description: `Gets the 7-day weather forecast in hourly and daily resolution for given location. Available worldwide.`,
+ inputSchema: openmeteo.GetV1ForecastParamsSchema
+ })
+ async getForecast(
+ params: openmeteo.GetV1ForecastParams
+ ): Promise {
+ const extractLocation = async (): Promise => {
+ if ('name' in params.location) {
+ const response = await this._geocode(params.location)
+ return pick(response, 'latitude', 'longitude')
+ }
+
+ return params.location
+ }
+
+ const { start, end } = validateAndSetDates(params.startDate, params.endDate)
+
+ return this.ky
+ .get('forecast', {
+ searchParams: sanitizeSearchParams({
+ ...(await extractLocation()),
+ temperature_unit: params.temperatureUnit,
+ start_date: toDateString(start),
+ end_date: toDateString(end),
+ current: [
+ 'temperature_2m',
+ 'rain',
+ 'relative_humidity_2m',
+ 'wind_speed_10m'
+ ],
+ daily: ['temperature_2m_max', 'temperature_2m_min', 'rain_sum'],
+ hourly: ['temperature_2m', 'relative_humidity_2m', 'rain'],
+ timezone: 'UTC'
+ })
+ })
+ .json()
+ }
+
+ protected async _geocode(
+ location: openmeteo.LocationSearch
+ ): Promise {
+ const { results } = await this.ky
+ .get('search', {
+ searchParams: sanitizeSearchParams({
+ name: location.name,
+ language: location.language,
+ country: location.country,
+ format: 'json',
+ count: 1
+ })
+ })
+ .json()
+
+ if (!results?.length) {
+ throw new Error(`No results found for location "${location.name}"`)
+ }
+
+ return results[0]
+ }
+}
+
+function toDateString(date: Date): string {
+ return date.toISOString().split('T')[0]!
+}
+
+function validateAndSetDates(
+ startDateStr: string,
+ endDateStr?: string
+): { start: Date; end: Date } {
+ const now = new Date()
+ const start = startDateStr
+ ? new Date(startDateStr)
+ : new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()))
+
+ if (endDateStr) {
+ const end = new Date(endDateStr)
+ if (end < start) {
+ throw new Error(
+ `The 'end_date' (${endDateStr}) has to occur on or after the 'start_date' (${startDateStr}).`
+ )
+ }
+ return { start, end }
+ } else {
+ // If endDate is undefined, set it to the start date
+ return { start, end: new Date(start) }
+ }
+}
diff --git a/legacy/packages/open-meteo/src/open-meteo.ts b/legacy/packages/open-meteo/src/open-meteo.ts
new file mode 100644
index 00000000..30119810
--- /dev/null
+++ b/legacy/packages/open-meteo/src/open-meteo.ts
@@ -0,0 +1,201 @@
+import { z } from 'zod'
+
+export namespace openmeteo {
+ export const apiBaseUrl = 'https://api.open-meteo.com/v1'
+
+ // -----------------------------------------------------------------------------
+ // Component schemas
+ // -----------------------------------------------------------------------------
+
+ /** For each selected weather variable, data will be returned as a floating point array. Additionally a `time` array will be returned with ISO8601 timestamps. */
+ export const HourlyResponseSchema = z
+ .object({
+ time: z.array(z.string()),
+ temperature_2m: z.array(z.number()).optional(),
+ relative_humidity_2m: z.array(z.number()).optional(),
+ dew_point_2m: z.array(z.number()).optional(),
+ apparent_temperature: z.array(z.number()).optional(),
+ pressure_msl: z.array(z.number()).optional(),
+ cloud_cover: z.array(z.number()).optional(),
+ cloud_cover_low: z.array(z.number()).optional(),
+ cloud_cover_mid: z.array(z.number()).optional(),
+ cloud_cover_high: z.array(z.number()).optional(),
+ wind_speed_10m: z.array(z.number()).optional(),
+ wind_speed_80m: z.array(z.number()).optional(),
+ wind_speed_120m: z.array(z.number()).optional(),
+ wind_speed_180m: z.array(z.number()).optional(),
+ wind_direction_10m: z.array(z.number()).optional(),
+ wind_direction_80m: z.array(z.number()).optional(),
+ wind_direction_120m: z.array(z.number()).optional(),
+ wind_direction_180m: z.array(z.number()).optional(),
+ wind_gusts_10m: z.array(z.number()).optional(),
+ shortwave_radiation: z.array(z.number()).optional(),
+ direct_radiation: z.array(z.number()).optional(),
+ direct_normal_irradiance: z.array(z.number()).optional(),
+ diffuse_radiation: z.array(z.number()).optional(),
+ vapour_pressure_deficit: z.array(z.number()).optional(),
+ evapotranspiration: z.array(z.number()).optional(),
+ precipitation: z.array(z.number()).optional(),
+ weather_code: z.array(z.number()).optional(),
+ snow_height: z.array(z.number()).optional(),
+ freezing_level_height: z.array(z.number()).optional(),
+ soil_temperature_0cm: z.array(z.number()).optional(),
+ soil_temperature_6cm: z.array(z.number()).optional(),
+ soil_temperature_18cm: z.array(z.number()).optional(),
+ soil_temperature_54cm: z.array(z.number()).optional(),
+ soil_moisture_0_1cm: z.array(z.number()).optional(),
+ soil_moisture_1_3cm: z.array(z.number()).optional(),
+ soil_moisture_3_9cm: z.array(z.number()).optional(),
+ soil_moisture_9_27cm: z.array(z.number()).optional(),
+ soil_moisture_27_81cm: z.array(z.number()).optional()
+ })
+ .describe(
+ 'For each selected weather variable, data will be returned as a floating point array. Additionally a `time` array will be returned with ISO8601 timestamps.'
+ )
+ export type HourlyResponse = z.infer
+
+ /** For each selected daily weather variable, data will be returned as a floating point array. Additionally a `time` array will be returned with ISO8601 timestamps. */
+ export const DailyResponseSchema = z
+ .object({
+ time: z.array(z.string()),
+ temperature_2m_max: z.array(z.number()).optional(),
+ temperature_2m_min: z.array(z.number()).optional(),
+ apparent_temperature_max: z.array(z.number()).optional(),
+ apparent_temperature_min: z.array(z.number()).optional(),
+ precipitation_sum: z.array(z.number()).optional(),
+ precipitation_hours: z.array(z.number()).optional(),
+ weather_code: z.array(z.number()).optional(),
+ sunrise: z.array(z.number()).optional(),
+ sunset: z.array(z.number()).optional(),
+ wind_speed_10m_max: z.array(z.number()).optional(),
+ wind_gusts_10m_max: z.array(z.number()).optional(),
+ wind_direction_10m_dominant: z.array(z.number()).optional(),
+ shortwave_radiation_sum: z.array(z.number()).optional(),
+ uv_index_max: z.array(z.number()).optional(),
+ uv_index_clear_sky_max: z.array(z.number()).optional(),
+ et0_fao_evapotranspiration: z.array(z.number()).optional()
+ })
+ .describe(
+ 'For each selected daily weather variable, data will be returned as a floating point array. Additionally a `time` array will be returned with ISO8601 timestamps.'
+ )
+ export type DailyResponse = z.infer
+
+ /** Current weather conditions with the attributes: time, temperature, wind_speed, wind_direction and weather_code */
+ export const CurrentWeatherSchema = z
+ .object({
+ time: z.string(),
+ temperature: z.number(),
+ wind_speed: z.number(),
+ wind_direction: z.number(),
+ weather_code: z.number().int()
+ })
+ .describe(
+ 'Current weather conditions with the attributes: time, temperature, wind_speed, wind_direction and weather_code'
+ )
+ export type CurrentWeather = z.infer
+
+ // -----------------------------------------------------------------------------
+ // Operation schemas
+ // -----------------------------------------------------------------------------
+
+ export const GetV1ForecastParamsSchema = z.object({
+ location: z.union([
+ z
+ .object({
+ name: z.string().min(1),
+ country: z.string().optional(),
+ language: z.string().default('English')
+ })
+ .strip(),
+ z
+ .object({
+ latitude: z.coerce.number(),
+ longitude: z.coerce.number()
+ })
+ .strip()
+ ]),
+ startDate: z
+ .string()
+ .date()
+ .describe(
+ 'Start date for the weather forecast in the format YYYY-MM-DD (UTC)'
+ ),
+ endDate: z
+ .string()
+ .date()
+ .describe(
+ 'End date for the weather forecast in the format YYYY-MM-DD (UTC)'
+ )
+ .optional(),
+ temperatureUnit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit')
+ })
+ export type GetV1ForecastParams = z.infer
+
+ export const GetV1ForecastResponseSchema = z.object({
+ /** WGS84 of the center of the weather grid-cell which was used to generate this forecast. This coordinate might be up to 5 km away. */
+ latitude: z
+ .number()
+ .describe(
+ 'WGS84 of the center of the weather grid-cell which was used to generate this forecast. This coordinate might be up to 5 km away.'
+ )
+ .optional(),
+ /** WGS84 of the center of the weather grid-cell which was used to generate this forecast. This coordinate might be up to 5 km away. */
+ longitude: z
+ .number()
+ .describe(
+ 'WGS84 of the center of the weather grid-cell which was used to generate this forecast. This coordinate might be up to 5 km away.'
+ )
+ .optional(),
+ /** The elevation in meters of the selected weather grid-cell. In mountain terrain it might differ from the location you would expect. */
+ elevation: z
+ .number()
+ .describe(
+ 'The elevation in meters of the selected weather grid-cell. In mountain terrain it might differ from the location you would expect.'
+ )
+ .optional(),
+ /** Generation time of the weather forecast in milli seconds. This is mainly used for performance monitoring and improvements. */
+ generationtime_ms: z
+ .number()
+ .describe(
+ 'Generation time of the weather forecast in milli seconds. This is mainly used for performance monitoring and improvements.'
+ )
+ .optional(),
+ /** Applied timezone offset from the &timezone= parameter. */
+ utc_offset_seconds: z
+ .number()
+ .int()
+ .describe('Applied timezone offset from the &timezone= parameter.')
+ .optional(),
+ hourly: HourlyResponseSchema.optional(),
+ /** For each selected weather variable, the unit will be listed here. */
+ hourly_units: z
+ .record(z.string())
+ .describe(
+ 'For each selected weather variable, the unit will be listed here.'
+ )
+ .optional(),
+ daily: DailyResponseSchema.optional(),
+ /** For each selected daily weather variable, the unit will be listed here. */
+ daily_units: z
+ .record(z.string())
+ .describe(
+ 'For each selected daily weather variable, the unit will be listed here.'
+ )
+ .optional(),
+ current_weather: CurrentWeatherSchema.optional()
+ })
+ export type GetV1ForecastResponse = z.infer<
+ typeof GetV1ForecastResponseSchema
+ >
+
+ export interface Location {
+ latitude: number
+ longitude: number
+ }
+
+ export interface LocationSearch {
+ name: string
+ country?: string
+ language?: string
+ }
+}
diff --git a/legacy/packages/open-meteo/tsconfig.json b/legacy/packages/open-meteo/tsconfig.json
new file mode 100644
index 00000000..6c8d720c
--- /dev/null
+++ b/legacy/packages/open-meteo/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "@agentic/tsconfig/base.json",
+ "include": ["src"],
+ "exclude": ["node_modules", "dist"]
+}