Skip to content

Commit

Permalink
feat(tool): update openMeteo tool
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomas2D committed Sep 11, 2024
1 parent 5a5114d commit 5726691
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 32 deletions.
84 changes: 52 additions & 32 deletions src/tools/weather/openMeteo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ interface LocationSearch {
language?: string;
}

interface Response {
export interface OpenMeteoToolResponse {
latitude: number;
longitude: number;
generationtime_ms: number;
Expand All @@ -56,9 +56,13 @@ interface Response {
daily: Record<string, any[]>;
}

export class OpenMeteoTool extends Tool<JSONToolOutput<Response>, ToolOptions, ToolRunOptions> {
export class OpenMeteoTool extends Tool<
JSONToolOutput<OpenMeteoToolResponse>,
ToolOptions,
ToolRunOptions
> {
name = "OpenMeteo";
description = `Retrieves current, previous, or upcoming weather for a given destination.`;
description = `Retrieves current, past, or future weather forecasts for a specified location.`;

inputSchema() {
return z
Expand All @@ -77,20 +81,16 @@ export class OpenMeteoTool extends Tool<JSONToolOutput<Response>, ToolOptions, T
})
.strip(),
),
elevation: z.number().nullish(),
timezone: z.string().default(Intl.DateTimeFormat().resolvedOptions().timeZone),
start_date: z
.string()
.date()
.describe("Date Format: YYYY-MM-DD (omit the field for the current date)")
.nullish(),
.describe("Start date for the weather forecast in the format YYYY-MM-DD (UTC)"),
end_date: z
.string()
.date()
.describe("Date Format: YYYY-MM-DD (omit the field for the current date)")
.nullish(),
forecast_days: z.number().int().min(0).max(16).default(1),
past_days: z.number().int().min(0).max(92).default(0),
.describe("End date for the weather forecast in the format YYYY-MM-DD (UTC)")
.optional(),
elevation: z.number().nullish(),
temperature_unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
})
.strip();
Expand All @@ -100,30 +100,50 @@ export class OpenMeteoTool extends Tool<JSONToolOutput<Response>, ToolOptions, T
this.register();
}

protected async _run({ location, ...input }: ToolInput<this>, options?: BaseToolRunOptions) {
protected async _run(
{ location, start_date: startDate, end_date: endDate, ...input }: ToolInput<this>,
options?: BaseToolRunOptions,
) {
const { apiKey } = this.options;

const extractLocation = async (): Promise<Location> => {
if ("name" in location) {
const response = await this._geocode(
{
name: location.name,
language: location.language,
},
options?.signal,
);
return pick(response, ["latitude", "longitude"]);
}
return location;
const prepareParams = async () => {
const extractLocation = async (): Promise<Location> => {
if ("name" in location) {
const response = await this._geocode(
{
name: location.name,
language: location.language,
},
options?.signal,
);
return pick(response, ["latitude", "longitude"]);
}
return location;
};

const start = startDate ? new Date(startDate) : new Date();
start.setHours(0, 0, 0, 0);
start.setTime(start.getTime() - start.getTimezoneOffset() * 60_000);

const end = endDate ? new Date(endDate) : new Date();
end.setHours(0, 0, 0, 0);
end.setTime(end.getTime() - end.getTimezoneOffset() * 60_000);

const toDateString = (date: Date) => date.toISOString().split("T")[0];

return createURLParams({
...pickBy(input, (v) => !isNullish(v) && v !== ""),
...(await extractLocation()),
start_date: toDateString(start),
end_date: toDateString(end),
current: ["temperature_2m", "rain", "apparent_temperature"],
daily: ["apparent_temperature_max", "apparent_temperature_min", "sunrise", "sunset"],
hourly: ["temperature_2m", "relative_humidity_2m", "apparent_temperature"],
timezone: "UTC",
});
};

const params = createURLParams({
...pickBy(input, (v) => !isNullish(v) && v !== ""),
...(await extractLocation()),
current: ["temperature_2m", "rain", "apparent_temperature"],
daily: ["apparent_temperature_max", "apparent_temperature_min", "sunrise", "sunset"],
hourly: ["temperature_2m", "relative_humidity_2m", "apparent_temperature"],
});
const params = await prepareParams();
const response = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`, {
headers: {
...(apiKey && {
Expand All @@ -139,7 +159,7 @@ export class OpenMeteoTool extends Tool<JSONToolOutput<Response>, ToolOptions, T
]);
}

const data: Response = await response.json();
const data: OpenMeteoToolResponse = await response.json();
return new JSONToolOutput(data);
}

Expand Down
62 changes: 62 additions & 0 deletions tests/e2e/tools/openMeteo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Copyright 2024 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { beforeEach, expect } from "vitest";
import { OpenMeteoTool } from "@/tools/weather/openMeteo.js";
import { ToolInputValidationError } from "@/tools/base.js";

describe("OpenMeteo", () => {
let instance: OpenMeteoTool;

beforeEach(() => {
instance = new OpenMeteoTool();
});

it("Runs", async () => {
const response = await instance.run(
{
location: {
name: "Boston",
},
start_date: "2023-09-10",
temperature_unit: "celsius",
},
{
signal: AbortSignal.timeout(60 * 1000),
retryOptions: {},
},
);

expect(response.isEmpty()).toBe(false);
expect(response.result).toMatchObject({
latitude: expect.any(Number),
longitude: expect.any(Number),
generationtime_ms: expect.any(Number),
utc_offset_seconds: 0,
timezone: "UTC",
timezone_abbreviation: "UTC",
});
});

it("Throws", async () => {
await expect(
instance.run({
location: { name: "Prague" },
start_date: "123",
}),
).rejects.toThrowError(ToolInputValidationError);
});
});

0 comments on commit 5726691

Please sign in to comment.