From 564de9b1b44658b6ba492ddcfd52f1a11075e605 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Tue, 25 Mar 2025 22:56:59 +0800 Subject: [PATCH] feat: add open-meteo.yaml to openapi-to-ts fixtures --- docs/mint.json | 1 + .../fixtures/generated/notion-client.ts | 27 +- .../fixtures/generated/open-meteo-client.ts | 74 +++ .../fixtures/generated/open-meteo.ts | 256 ++++++++++ .../fixtures/openapi/open-meteo.yaml | 461 ++++++++++++++++++ .../generate-ts-from-openapi.test.ts.snap | 335 +++++++++++++ .../src/generate-ts-from-openapi.test.ts | 1 + .../src/generate-ts-from-openapi.ts | 5 +- packages/openapi-to-ts/tsconfig.json | 7 +- packages/stdlib/package.json | 1 + packages/stdlib/src/index.ts | 1 + pnpm-lock.yaml | 19 + readme.md | 1 + 13 files changed, 1174 insertions(+), 15 deletions(-) create mode 100644 packages/openapi-to-ts/fixtures/generated/open-meteo-client.ts create mode 100644 packages/openapi-to-ts/fixtures/generated/open-meteo.ts create mode 100644 packages/openapi-to-ts/fixtures/openapi/open-meteo.yaml diff --git a/docs/mint.json b/docs/mint.json index 8dd1f38..caa762e 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -77,6 +77,7 @@ "tools/mcp", "tools/notion", "tools/novu", + "tools/open-meteo", "tools/people-data-labs", "tools/perigon", "tools/polygon", diff --git a/packages/openapi-to-ts/fixtures/generated/notion-client.ts b/packages/openapi-to-ts/fixtures/generated/notion-client.ts index 9e84797..7ced097 100644 --- a/packages/openapi-to-ts/fixtures/generated/notion-client.ts +++ b/packages/openapi-to-ts/fixtures/generated/notion-client.ts @@ -3,14 +3,15 @@ */ import { - AIFunctionsProvider, aiFunction, + AIFunctionsProvider, assert, getEnv, pick, sanitizeSearchParams } from '@agentic/core' import defaultKy, { type KyInstance } from 'ky' + import { notion } from './notion' /** @@ -73,7 +74,7 @@ export class NotionClient extends AIFunctionsProvider { }) async getUser(params: notion.GetUserParams): Promise { return this.ky - .get(`/users/${params['user_id']}`) + .get(`/users/${params.user_id}`) .json() } @@ -125,7 +126,7 @@ export class NotionClient extends AIFunctionsProvider { }) async getPage(params: notion.GetPageParams): Promise { return this.ky - .get(`/pages/${params['page_id']}`, { + .get(`/pages/${params.page_id}`, { searchParams: sanitizeSearchParams(pick(params, 'filter_properties')) }) .json() @@ -143,7 +144,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.UpdatePageParams ): Promise { return this.ky - .patch(`/pages/${params['page_id']}`, { + .patch(`/pages/${params.page_id}`, { json: pick(params, 'properties', 'archived') }) .json() @@ -161,7 +162,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.GetPagePropertyParams ): Promise { return this.ky - .get(`/pages/${params['page_id']}/properties/${params['property_id']}`, { + .get(`/pages/${params.page_id}/properties/${params.property_id}`, { searchParams: sanitizeSearchParams( pick(params, 'start_cursor', 'page_size') ) @@ -181,7 +182,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.GetBlockParams ): Promise { return this.ky - .get(`/blocks/${params['block_id']}`) + .get(`/blocks/${params.block_id}`) .json() } @@ -197,7 +198,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.DeleteBlockParams ): Promise { return this.ky - .delete(`/blocks/${params['block_id']}`) + .delete(`/blocks/${params.block_id}`) .json() } @@ -213,7 +214,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.UpdateBlockParams ): Promise { return this.ky - .patch(`/blocks/${params['block_id']}`, { + .patch(`/blocks/${params.block_id}`, { json: pick( params, 'paragraph', @@ -258,7 +259,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.ListBlockChildrenParams ): Promise { return this.ky - .get(`/blocks/${params['block_id']}/children`, { + .get(`/blocks/${params.block_id}/children`, { searchParams: sanitizeSearchParams( pick(params, 'start_cursor', 'page_size') ) @@ -278,7 +279,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.AppendBlockChildrenParams ): Promise { return this.ky - .patch(`/blocks/${params['block_id']}/children`, { + .patch(`/blocks/${params.block_id}/children`, { json: pick(params, 'children') }) .json() @@ -296,7 +297,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.GetDatabaseParams ): Promise { return this.ky - .get(`/databases/${params['database_id']}`) + .get(`/databases/${params.database_id}`) .json() } @@ -312,7 +313,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.UpdateDatabaseParams ): Promise { return this.ky - .patch(`/databases/${params['database_id']}`, { + .patch(`/databases/${params.database_id}`, { json: pick( params, 'title', @@ -339,7 +340,7 @@ export class NotionClient extends AIFunctionsProvider { params: notion.QueryDatabaseParams ): Promise { return this.ky - .post(`/databases/${params['database_id']}/query`, { + .post(`/databases/${params.database_id}/query`, { searchParams: sanitizeSearchParams(pick(params, 'filter_properties')), json: pick( params, diff --git a/packages/openapi-to-ts/fixtures/generated/open-meteo-client.ts b/packages/openapi-to-ts/fixtures/generated/open-meteo-client.ts new file mode 100644 index 0000000..a67f78d --- /dev/null +++ b/packages/openapi-to-ts/fixtures/generated/open-meteo-client.ts @@ -0,0 +1,74 @@ +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { + aiFunction, + AIFunctionsProvider, + pick, + sanitizeSearchParams +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' + +import { openmeteo } from './open-meteo' + +/** + * Agentic OpenMeteo client. + * + * Open-Meteo offers free weather forecast APIs for open-source developers and non-commercial use. No API key is required. + */ +export class OpenMeteoClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + + protected readonly apiBaseUrl: string + + constructor({ + apiBaseUrl, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + super() + + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl + }) + } + + /** + * 7 day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide. + */ + @aiFunction({ + name: 'open_meteo_get_v1_forecast', + description: `7 day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide.`, + inputSchema: openmeteo.GetV1ForecastParamsSchema, + tags: ['Weather Forecast APIs'] + }) + async getV1Forecast( + params: openmeteo.GetV1ForecastParams + ): Promise { + return this.ky + .get('/v1/forecast', { + searchParams: sanitizeSearchParams( + pick( + params, + 'hourly', + 'daily', + 'latitude', + 'longitude', + 'current_weather', + 'temperature_unit', + 'wind_speed_unit', + 'timeformat', + 'timezone', + 'past_days' + ) + ) + }) + .json() + } +} diff --git a/packages/openapi-to-ts/fixtures/generated/open-meteo.ts b/packages/openapi-to-ts/fixtures/generated/open-meteo.ts new file mode 100644 index 0000000..8ddc4f4 --- /dev/null +++ b/packages/openapi-to-ts/fixtures/generated/open-meteo.ts @@ -0,0 +1,256 @@ +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { z } from 'zod' + +export namespace openmeteo { + // ----------------------------------------------------------------------------- + // 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({ + hourly: z + .array( + z.enum([ + 'temperature_2m', + 'relative_humidity_2m', + 'dew_point_2m', + 'apparent_temperature', + 'pressure_msl', + 'cloud_cover', + 'cloud_cover_low', + 'cloud_cover_mid', + 'cloud_cover_high', + 'wind_speed_10m', + 'wind_speed_80m', + 'wind_speed_120m', + 'wind_speed_180m', + 'wind_direction_10m', + 'wind_direction_80m', + 'wind_direction_120m', + 'wind_direction_180m', + 'wind_gusts_10m', + 'shortwave_radiation', + 'direct_radiation', + 'direct_normal_irradiance', + 'diffuse_radiation', + 'vapour_pressure_deficit', + 'evapotranspiration', + 'precipitation', + 'weather_code', + 'snow_height', + 'freezing_level_height', + 'soil_temperature_0cm', + 'soil_temperature_6cm', + 'soil_temperature_18cm', + 'soil_temperature_54cm', + 'soil_moisture_0_1cm', + 'soil_moisture_1_3cm', + 'soil_moisture_3_9cm', + 'soil_moisture_9_27cm', + 'soil_moisture_27_81cm' + ]) + ) + .optional(), + daily: z + .array( + z.enum([ + 'temperature_2m_max', + 'temperature_2m_min', + 'apparent_temperature_max', + 'apparent_temperature_min', + 'precipitation_sum', + 'precipitation_hours', + 'weather_code', + 'sunrise', + 'sunset', + 'wind_speed_10m_max', + 'wind_gusts_10m_max', + 'wind_direction_10m_dominant', + 'shortwave_radiation_sum', + 'uv_index_max', + 'uv_index_clear_sky_max', + 'et0_fao_evapotranspiration' + ]) + ) + .optional(), + /** WGS84 coordinate */ + latitude: z.number().describe('WGS84 coordinate'), + /** WGS84 coordinate */ + longitude: z.number().describe('WGS84 coordinate'), + current_weather: z.boolean().optional(), + temperature_unit: z.enum(['celsius', 'fahrenheit']).default('celsius'), + wind_speed_unit: z.enum(['kmh', 'ms', 'mph', 'kn']).default('kmh'), + /** If format `unixtime` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply `utc_offset_seconds` again to get the correct date. */ + timeformat: z + .enum(['iso8601', 'unixtime']) + .describe( + 'If format `unixtime` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply `utc_offset_seconds` again to get the correct date.' + ) + .default('iso8601'), + /** If `timezone` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported. */ + timezone: z + .string() + .describe( + 'If `timezone` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported.' + ) + .optional(), + /** If `past_days` is set, yesterdays or the day before yesterdays data are also returned. */ + past_days: z + .union([z.literal(1), z.literal(2)]) + .describe( + 'If `past_days` is set, yesterdays or the day before yesterdays data are also returned.' + ) + .optional() + }) + 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 + > +} diff --git a/packages/openapi-to-ts/fixtures/openapi/open-meteo.yaml b/packages/openapi-to-ts/fixtures/openapi/open-meteo.yaml new file mode 100644 index 0000000..d99f635 --- /dev/null +++ b/packages/openapi-to-ts/fixtures/openapi/open-meteo.yaml @@ -0,0 +1,461 @@ +openapi: 3.0.0 +info: + title: Open-Meteo APIs + description: 'Open-Meteo offers free weather forecast APIs for open-source developers and non-commercial use. No API key is required.' + version: '1.0' + contact: + name: Open-Meteo + url: https://open-meteo.com + email: info@open-meteo.com + license: + name: Attribution 4.0 International (CC BY 4.0) + url: https://creativecommons.org/licenses/by/4.0/ + termsOfService: https://open-meteo.com/en/features#terms +paths: + /v1/forecast: + servers: + - url: https://api.open-meteo.com + get: + tags: + - Weather Forecast APIs + summary: 7 day weather forecast for coordinates + description: 7 day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide. + parameters: + - name: hourly + in: query + explode: false + schema: + type: array + items: + type: string + enum: + - temperature_2m + - relative_humidity_2m + - dew_point_2m + - apparent_temperature + - pressure_msl + - cloud_cover + - cloud_cover_low + - cloud_cover_mid + - cloud_cover_high + - wind_speed_10m + - wind_speed_80m + - wind_speed_120m + - wind_speed_180m + - wind_direction_10m + - wind_direction_80m + - wind_direction_120m + - wind_direction_180m + - wind_gusts_10m + - shortwave_radiation + - direct_radiation + - direct_normal_irradiance + - diffuse_radiation + - vapour_pressure_deficit + - evapotranspiration + - precipitation + - weather_code + - snow_height + - freezing_level_height + - soil_temperature_0cm + - soil_temperature_6cm + - soil_temperature_18cm + - soil_temperature_54cm + - soil_moisture_0_1cm + - soil_moisture_1_3cm + - soil_moisture_3_9cm + - soil_moisture_9_27cm + - soil_moisture_27_81cm + - name: daily + in: query + schema: + type: array + items: + type: string + enum: + - temperature_2m_max + - temperature_2m_min + - apparent_temperature_max + - apparent_temperature_min + - precipitation_sum + - precipitation_hours + - weather_code + - sunrise + - sunset + - wind_speed_10m_max + - wind_gusts_10m_max + - wind_direction_10m_dominant + - shortwave_radiation_sum + - uv_index_max + - uv_index_clear_sky_max + - et0_fao_evapotranspiration + - name: latitude + in: query + required: true + description: 'WGS84 coordinate' + schema: + type: number + format: double + - name: longitude + in: query + required: true + description: 'WGS84 coordinate' + schema: + type: number + format: double + - name: current_weather + in: query + schema: + type: boolean + - name: temperature_unit + in: query + schema: + type: string + default: celsius + enum: + - celsius + - fahrenheit + - name: wind_speed_unit + in: query + schema: + type: string + default: kmh + enum: + - kmh + - ms + - mph + - kn + - name: timeformat + in: query + description: If format `unixtime` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply `utc_offset_seconds` again to get the correct date. + schema: + type: string + default: iso8601 + enum: + - iso8601 + - unixtime + - name: timezone + in: query + description: If `timezone` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported. + schema: + type: string + - name: past_days + in: query + description: If `past_days` is set, yesterdays or the day before yesterdays data are also returned. + schema: + type: integer + enum: + - 1 + - 2 + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + latitude: + type: number + example: 52.52 + description: 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: + type: number + example: 13.419.52 + description: 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. + elevation: + type: number + example: 44.812 + description: The elevation in meters of the selected weather grid-cell. In mountain terrain it might differ from the location you would expect. + generationtime_ms: + type: number + example: 2.2119 + description: Generation time of the weather forecast in milli seconds. This is mainly used for performance monitoring and improvements. + utc_offset_seconds: + type: integer + example: 3600 + description: Applied timezone offset from the &timezone= parameter. + hourly: + $ref: '#/components/schemas/HourlyResponse' + hourly_units: + type: object + additionalProperties: + type: string + description: For each selected weather variable, the unit will be listed here. + daily: + $ref: '#/components/schemas/DailyResponse' + daily_units: + type: object + additionalProperties: + type: string + description: For each selected daily weather variable, the unit will be listed here. + current_weather: + $ref: '#/components/schemas/CurrentWeather' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + properties: + error: + type: boolean + description: Always set true for errors + reason: + type: string + description: Description of the error + example: 'Latitude must be in range of -90 to 90°. Given: 300' +components: + schemas: + HourlyResponse: + type: object + description: For each selected weather variable, data will be returned as a floating point array. Additionally a `time` array will be returned with ISO8601 timestamps. + required: + - time + properties: + time: + type: array + items: + type: string + temperature_2m: + type: array + items: + type: number + relative_humidity_2m: + type: array + items: + type: number + dew_point_2m: + type: array + items: + type: number + apparent_temperature: + type: array + items: + type: number + pressure_msl: + type: array + items: + type: number + cloud_cover: + type: array + items: + type: number + cloud_cover_low: + type: array + items: + type: number + cloud_cover_mid: + type: array + items: + type: number + cloud_cover_high: + type: array + items: + type: number + wind_speed_10m: + type: array + items: + type: number + wind_speed_80m: + type: array + items: + type: number + wind_speed_120m: + type: array + items: + type: number + wind_speed_180m: + type: array + items: + type: number + wind_direction_10m: + type: array + items: + type: number + wind_direction_80m: + type: array + items: + type: number + wind_direction_120m: + type: array + items: + type: number + wind_direction_180m: + type: array + items: + type: number + wind_gusts_10m: + type: array + items: + type: number + shortwave_radiation: + type: array + items: + type: number + direct_radiation: + type: array + items: + type: number + direct_normal_irradiance: + type: array + items: + type: number + diffuse_radiation: + type: array + items: + type: number + vapour_pressure_deficit: + type: array + items: + type: number + evapotranspiration: + type: array + items: + type: number + precipitation: + type: array + items: + type: number + weather_code: + type: array + items: + type: number + snow_height: + type: array + items: + type: number + freezing_level_height: + type: array + items: + type: number + soil_temperature_0cm: + type: array + items: + type: number + soil_temperature_6cm: + type: array + items: + type: number + soil_temperature_18cm: + type: array + items: + type: number + soil_temperature_54cm: + type: array + items: + type: number + soil_moisture_0_1cm: + type: array + items: + type: number + soil_moisture_1_3cm: + type: array + items: + type: number + soil_moisture_3_9cm: + type: array + items: + type: number + soil_moisture_9_27cm: + type: array + items: + type: number + soil_moisture_27_81cm: + type: array + items: + type: number + DailyResponse: + type: object + description: 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. + properties: + time: + type: array + items: + type: string + temperature_2m_max: + type: array + items: + type: number + temperature_2m_min: + type: array + items: + type: number + apparent_temperature_max: + type: array + items: + type: number + apparent_temperature_min: + type: array + items: + type: number + precipitation_sum: + type: array + items: + type: number + precipitation_hours: + type: array + items: + type: number + weather_code: + type: array + items: + type: number + sunrise: + type: array + items: + type: number + sunset: + type: array + items: + type: number + wind_speed_10m_max: + type: array + items: + type: number + wind_gusts_10m_max: + type: array + items: + type: number + wind_direction_10m_dominant: + type: array + items: + type: number + shortwave_radiation_sum: + type: array + items: + type: number + uv_index_max: + type: array + items: + type: number + uv_index_clear_sky_max: + type: array + items: + type: number + et0_fao_evapotranspiration: + type: array + items: + type: number + required: + - time + CurrentWeather: + type: object + description: 'Current weather conditions with the attributes: time, temperature, wind_speed, wind_direction and weather_code' + properties: + time: + type: string + temperature: + type: number + wind_speed: + type: number + wind_direction: + type: number + weather_code: + type: integer + required: + - time + - temperature + - wind_speed + - wind_direction + - weather_code diff --git a/packages/openapi-to-ts/src/__snapshots__/generate-ts-from-openapi.test.ts.snap b/packages/openapi-to-ts/src/__snapshots__/generate-ts-from-openapi.test.ts.snap index c893e17..fea2cf4 100644 --- a/packages/openapi-to-ts/src/__snapshots__/generate-ts-from-openapi.test.ts.snap +++ b/packages/openapi-to-ts/src/__snapshots__/generate-ts-from-openapi.test.ts.snap @@ -2579,6 +2579,341 @@ export class NotionClient extends AIFunctionsProvider { " `; +exports[`openapi-to-ts > open-meteo.yaml 1`] = ` +"/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { z } from 'zod' + +export namespace openmeteo { + // ----------------------------------------------------------------------------- + // 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({ + hourly: z + .array( + z.enum([ + 'temperature_2m', + 'relative_humidity_2m', + 'dew_point_2m', + 'apparent_temperature', + 'pressure_msl', + 'cloud_cover', + 'cloud_cover_low', + 'cloud_cover_mid', + 'cloud_cover_high', + 'wind_speed_10m', + 'wind_speed_80m', + 'wind_speed_120m', + 'wind_speed_180m', + 'wind_direction_10m', + 'wind_direction_80m', + 'wind_direction_120m', + 'wind_direction_180m', + 'wind_gusts_10m', + 'shortwave_radiation', + 'direct_radiation', + 'direct_normal_irradiance', + 'diffuse_radiation', + 'vapour_pressure_deficit', + 'evapotranspiration', + 'precipitation', + 'weather_code', + 'snow_height', + 'freezing_level_height', + 'soil_temperature_0cm', + 'soil_temperature_6cm', + 'soil_temperature_18cm', + 'soil_temperature_54cm', + 'soil_moisture_0_1cm', + 'soil_moisture_1_3cm', + 'soil_moisture_3_9cm', + 'soil_moisture_9_27cm', + 'soil_moisture_27_81cm' + ]) + ) + .optional(), + daily: z + .array( + z.enum([ + 'temperature_2m_max', + 'temperature_2m_min', + 'apparent_temperature_max', + 'apparent_temperature_min', + 'precipitation_sum', + 'precipitation_hours', + 'weather_code', + 'sunrise', + 'sunset', + 'wind_speed_10m_max', + 'wind_gusts_10m_max', + 'wind_direction_10m_dominant', + 'shortwave_radiation_sum', + 'uv_index_max', + 'uv_index_clear_sky_max', + 'et0_fao_evapotranspiration' + ]) + ) + .optional(), + /** WGS84 coordinate */ + latitude: z.number().describe('WGS84 coordinate'), + /** WGS84 coordinate */ + longitude: z.number().describe('WGS84 coordinate'), + current_weather: z.boolean().optional(), + temperature_unit: z.enum(['celsius', 'fahrenheit']).default('celsius'), + wind_speed_unit: z.enum(['kmh', 'ms', 'mph', 'kn']).default('kmh'), + /** If format \`unixtime\` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply \`utc_offset_seconds\` again to get the correct date. */ + timeformat: z + .enum(['iso8601', 'unixtime']) + .describe( + 'If format \`unixtime\` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply \`utc_offset_seconds\` again to get the correct date.' + ) + .default('iso8601'), + /** If \`timezone\` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported. */ + timezone: z + .string() + .describe( + 'If \`timezone\` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported.' + ) + .optional(), + /** If \`past_days\` is set, yesterdays or the day before yesterdays data are also returned. */ + past_days: z + .union([z.literal(1), z.literal(2)]) + .describe( + 'If \`past_days\` is set, yesterdays or the day before yesterdays data are also returned.' + ) + .optional() + }) + 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 + > +} + + +/** + * This file was auto-generated from an OpenAPI spec. + */ + +import { + AIFunctionsProvider, + aiFunction, + pick, + sanitizeSearchParams +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { openmeteo } from './open-meteo' + +/** + * Agentic OpenMeteo client. + * + * Open-Meteo offers free weather forecast APIs for open-source developers and non-commercial use. No API key is required. + */ +export class OpenMeteoClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + + protected readonly apiBaseUrl: string + + constructor({ + apiBaseUrl, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + super() + + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl + }) + } + + /** + * 7 day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide. + */ + @aiFunction({ + name: 'open_meteo_get_v1_forecast', + description: \`7 day weather variables in hourly and daily resolution for given WGS84 latitude and longitude coordinates. Available worldwide.\`, + inputSchema: openmeteo.GetV1ForecastParamsSchema, + tags: ['Weather Forecast APIs'] + }) + async getV1Forecast( + params: openmeteo.GetV1ForecastParams + ): Promise { + return this.ky + .get('/v1/forecast', { + searchParams: sanitizeSearchParams( + pick( + params, + 'hourly', + 'daily', + 'latitude', + 'longitude', + 'current_weather', + 'temperature_unit', + 'wind_speed_unit', + 'timeformat', + 'timezone', + 'past_days' + ) + ) + }) + .json() + } +} +" +`; + exports[`openapi-to-ts > pet-store.json 1`] = ` "/** * This file was auto-generated from an OpenAPI spec. diff --git a/packages/openapi-to-ts/src/generate-ts-from-openapi.test.ts b/packages/openapi-to-ts/src/generate-ts-from-openapi.test.ts index 5956075..f1827bf 100644 --- a/packages/openapi-to-ts/src/generate-ts-from-openapi.test.ts +++ b/packages/openapi-to-ts/src/generate-ts-from-openapi.test.ts @@ -9,6 +9,7 @@ const fixtures = [ 'firecrawl.json', // 'github.json', // TODO: not working 100% yet 'notion.json', + 'open-meteo.yaml', 'pet-store.json', 'petstore-expanded.json', 'security.json', diff --git a/packages/openapi-to-ts/src/generate-ts-from-openapi.ts b/packages/openapi-to-ts/src/generate-ts-from-openapi.ts index a342ab6..2193f38 100644 --- a/packages/openapi-to-ts/src/generate-ts-from-openapi.ts +++ b/packages/openapi-to-ts/src/generate-ts-from-openapi.ts @@ -61,7 +61,10 @@ export async function generateTSFromOpenAPI({ throw new Error(`Unexpected OpenAPI version "${spec.openapi}"`) } - const openapiSpecName = path.basename(openapiFilePath, '.json') + const openapiSpecName = path + .basename(openapiFilePath) + .replace(/\.json$/, '') + .replace(/\.yaml$/, '') assert( openapiSpecName.toLowerCase() === openapiSpecName, `OpenAPI spec name "${openapiSpecName}" must be in kebab case` diff --git a/packages/openapi-to-ts/tsconfig.json b/packages/openapi-to-ts/tsconfig.json index a05348a..fd963e7 100644 --- a/packages/openapi-to-ts/tsconfig.json +++ b/packages/openapi-to-ts/tsconfig.json @@ -1,5 +1,10 @@ { "extends": "@agentic/tsconfig/base.json", - "include": ["src", "bin"], + "include": [ + "src", + "bin", + "../open-meteo/src/open-meteo-client.ts", + "../open-meteo/src/open-meteo.ts" + ], "exclude": ["node_modules", "dist"] } diff --git a/packages/stdlib/package.json b/packages/stdlib/package.json index c6e044e..2d90a06 100644 --- a/packages/stdlib/package.json +++ b/packages/stdlib/package.json @@ -55,6 +55,7 @@ "@agentic/mcp": "workspace:*", "@agentic/notion": "workspace:*", "@agentic/novu": "workspace:*", + "@agentic/open-meteo": "workspace:*", "@agentic/people-data-labs": "workspace:*", "@agentic/perigon": "workspace:*", "@agentic/polygon": "workspace:*", diff --git a/packages/stdlib/src/index.ts b/packages/stdlib/src/index.ts index 6936560..343f3d8 100644 --- a/packages/stdlib/src/index.ts +++ b/packages/stdlib/src/index.ts @@ -21,6 +21,7 @@ export * from '@agentic/mcp' export * from '@agentic/midjourney' export * from '@agentic/notion' export * from '@agentic/novu' +export * from '@agentic/open-meteo' export * from '@agentic/people-data-labs' export * from '@agentic/perigon' export * from '@agentic/polygon' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10c279c..9ee51b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1012,6 +1012,22 @@ importers: specifier: workspace:* version: link:../tsconfig + packages/open-meteo: + dependencies: + '@agentic/core': + specifier: workspace:* + version: link:../core + ky: + specifier: 'catalog:' + version: 1.7.5 + zod: + specifier: 'catalog:' + version: 3.24.2 + devDependencies: + '@agentic/tsconfig': + specifier: workspace:* + version: link:../tsconfig + packages/openapi-to-ts: dependencies: '@agentic/core': @@ -1320,6 +1336,9 @@ importers: '@agentic/novu': specifier: workspace:* version: link:../novu + '@agentic/open-meteo': + specifier: workspace:* + version: link:../open-meteo '@agentic/people-data-labs': specifier: workspace:* version: link:../people-data-labs diff --git a/readme.md b/readme.md index 9e03fa1..d96f56e 100644 --- a/readme.md +++ b/readme.md @@ -202,6 +202,7 @@ Full docs are available at [agentic.so](https://agentic.so). | [McpTools](https://modelcontextprotocol.io) | `@agentic/mcp` | [docs](https://agentic.so/tools/mcp) | Model Context Protocol (MCP) client, supporting any MCP server. Use [createMcpTools](https://agentic.so/tools/mcp) to spawn or connect to an MCP server. | | [Notion](https://developers.notion.com/docs) | `@agentic/notion` | [docs](https://agentic.so/tools/notion) | Official Notion API for accessing pages, databases, and content. | | [Novu](https://novu.co) | `@agentic/novu` | [docs](https://agentic.so/tools/novu) | Sending notifications (email, SMS, in-app, push, etc). | +| [Open Meteo](https://open-meteo.com) | `@agentic/open-meteo` | [docs](https://agentic.so/tools/open-meteo) | Free weather API (no API key required). | | [People Data Labs](https://www.peopledatalabs.com) | `@agentic/people-data-labs` | [docs](https://agentic.so/tools/people-data-labs) | People & company data (WIP). | | [Perigon](https://www.goperigon.com/products/news-api) | `@agentic/perigon` | [docs](https://agentic.so/tools/perigon) | Real-time news API and web content data from 140,000+ sources. Structured and enriched by AI, primed for LLMs. | | [Polygon](https://polygon.io) | `@agentic/polygon` | [docs](https://agentic.so/tools/polygon) | Stock market and company financial data. |