ai: add common tool runtime and built-in tools

This commit is contained in:
2026-05-10 22:52:48 +03:00
parent 32c35f54aa
commit 355ae8e5da
14 changed files with 2864 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
import axios from "axios";
import {Environment} from "../../common/environment";
import {logError} from "../../util/utils";
import {AiTool} from "../tool-types";
import {asNonEmptyString} from "./utils";
export const getWeatherTool = {
type: "function",
function: {
name: "get_weather",
description: "Get the current temperature for a city.",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "The name of the city."
},
lang: {
type: "string",
description: "language code for the response/content. Must be a valid ISO 639-1 two-letter language code, for example: \"en\", \"ru\", \"de\", \"fr\".Determine the value automatically from the language the user is using to communicate with the LLM. If the user explicitly requests a specific language, use that requested language instead. Do not use language names, locales, or regional variants such as \"English\", \"ru-RU\", or \"en_US\"; return only the ISO 639-1 code."
}
},
required: ["city", "lang"],
}
}
} satisfies AiTool;
export const weatherToolPrompt = [
"Weather tool rules:",
"- Use `get_weather` for current weather, current temperature, conditions, hot/cold/rainy/snowy questions, and weather follow-ups.",
"- Weather is live/current data. Never answer it from memory.",
"- A weather tool result is valid only for the exact city used in that tool call.",
"- If the user changes the city, call `get_weather` again.",
"- Follow-up questions like `what about Moscow?`, `and for Krasnodar?`, `what about there?`, `what about Berlin?` inherit the previous weather intent and require a new tool call for the new city.",
"",
"Arguments:",
"- `city`: the city from the latest user request or resolved from the follow-up context.",
"- `lang`: ISO 639-1 two-letter language code only: `ru`, `en`, `de`, etc.",
"",
"Do not guess, compare, or reuse weather from another city.",
"If the city is missing or unclear, ask the user to specify it.",
].join("\n");
export async function getWeather(args?: Record<string, unknown>): Promise<any | null> {
console.log("getWeather()");
try {
const city = asNonEmptyString(args?.city);
const lang = asNonEmptyString(args?.lang);
if (!city) {
return null;
}
const apiKey = Environment.OPEN_WEATHER_MAP_API_KEY;
const geocodeResponse = (await axios.get("https://api.openweathermap.org/geo/1.0/direct", {
params: {
q: city,
limit: 1,
appid: apiKey,
},
})).data[0];
console.log("GEOCODE_RESPONSE", geocodeResponse);
if (!geocodeResponse) {
return {
ok: false,
tool: "get_weather",
error: `City not found: ${city}`,
city,
lang,
};
}
const lat = geocodeResponse.lat;
const lon = geocodeResponse.lon;
const response = (await axios.get("https://api.openweathermap.org/data/2.5/weather", {
params: {
lat,
lon,
units: "metric",
appid: apiKey,
...(lang ? {lang} : {}),
},
})).data;
console.log("RESPONSE: getWeather(lang=" + lang + "): ", response);
const main = response.main;
const sys = response.sys;
const wind = response.wind;
const weather = response.weather[0];
let date = new Date(sys.sunrise * 1000);
const sunrise = [
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
]
.map((v) => String(v).padStart(2, "0"))
.join(":");
date = new Date(sys.sunset * 1000);
const sunset = [
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
]
.map((v) => String(v).padStart(2, "0"))
.join(":");
return {
ok: true,
tool: "get_weather",
scope: {
city,
lang,
validOnlyForExactCity: true,
liveData: true,
note: "If the user asks about another city, call get_weather again.",
},
weather: {
main: weather.main,
description: weather.description,
temperature: main.temp,
temperatureMax: main.temp_max,
temperatureMin: main.temp_min,
feelsLike: main.feels_like,
humidity: main.humidity,
pressure: main.pressure,
seaLevel: main.sea_level ?? null,
groundLevel: main.grnd_level ?? null,
sunriseUtc: sunrise,
sunsetUtc: sunset,
windDegree: wind.deg,
windSpeed: wind.speed,
},
};
} catch (e: any) {
logError(e);
return null;
} finally {
console.log("END: getWeather()");
}
}