src / toolsProvider.ts
import { text, tool, ToolsProviderController } from "@lmstudio/sdk";
import { z } from "zod";
import { configSchematics } from "./configSchematics";
// Helper function to format date according to config
function formatDate(date: Date, format: string): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
switch (format) {
case "DD/MM/YYYY":
return `${day}/${month}/${year}`;
case "MM/DD/YYYY":
return `${month}/${day}/${year}`;
case "YYYY-MM-DD":
default:
return `${year}-${month}-${day}`;
}
}
// Helper function to format time according to config
function formatTime(date: Date, format: string): string {
const hours = date.getHours();
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
if (format === "12h") {
const period = hours >= 12 ? "PM" : "AM";
const hours12 = hours % 12 || 12;
return `${hours12}:${minutes}:${seconds} ${period}`;
}
return `${String(hours).padStart(2, '0')}:${minutes}:${seconds}`;
}
// Get timezone offset string
function getTimezoneOffset(timezone: string): string {
try {
const date = new Date();
const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
const offset = (tzDate.getTime() - utcDate.getTime()) / (1000 * 60 * 60);
const sign = offset >= 0 ? "+" : "";
return `UTC${sign}${offset}`;
} catch {
return "Unknown";
}
}
export async function toolsProvider(ctl: ToolsProviderController) {
const config = ctl.getPluginConfig(configSchematics);
// Tool 1: Get current time
const getCurrentTimeTool = tool({
name: "get_current_time",
description: text`
Gets the current time and date. Can specify a timezone to get time for that specific timezone.
If no timezone is specified, uses the default timezone from configuration.
Examples of valid timezones: 'UTC', 'America/New_York', 'Europe/London',
'Europe/Moscow', 'Asia/Tokyo', 'Australia/Sydney', 'Pacific/Auckland'
`,
parameters: {
timezone: z.string().optional().describe("Timezone to get time for (optional)")
},
implementation: async ({ timezone }) => {
const targetTimezone = timezone || config.get("defaultTimezone");
const timeFormat = config.get("timeFormat");
const dateFormat = config.get("dateFormat");
try {
const now = new Date();
const options: Intl.DateTimeFormatOptions = {
timeZone: targetTimezone,
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: timeFormat === "12h"
};
const formattedTime = now.toLocaleString("en-US", options);
const offset = getTimezoneOffset(targetTimezone);
return {
timezone: targetTimezone,
offset: offset,
datetime: formattedTime,
iso8601: now.toISOString(),
unix_timestamp: Math.floor(now.getTime() / 1000),
day_of_week: now.toLocaleString("en-US", { weekday: 'long', timeZone: targetTimezone }),
week_number: getWeekNumber(now, targetTimezone)
};
} catch (error) {
return `Error: Invalid timezone "${targetTimezone}". Please use a valid IANA timezone.`;
}
},
});
// Tool 2: Get calendar information
const getCalendarTool = tool({
name: "get_calendar",
description: text`
Gets calendar information for a specific month and year.
Returns the calendar grid, number of days, weekdays, and other calendar-related data.
If no month/year specified, returns current month's calendar.
`,
parameters: {
month: z.number().min(1).max(12).optional().describe("Month (1-12)"),
year: z.number().optional().describe("Year (e.g., 2024)"),
timezone: z.string().optional().describe("Timezone (optional)")
},
implementation: async ({ month, year, timezone }) => {
const targetTimezone = timezone || config.get("defaultTimezone");
const now = new Date();
const targetMonth = month || (now.getMonth() + 1);
const targetYear = year || now.getFullYear();
try {
const calendar = generateCalendar(targetYear, targetMonth, targetTimezone);
const monthName = new Date(targetYear, targetMonth - 1).toLocaleString("en-US", { month: 'long' });
return {
month: targetMonth,
month_name: monthName,
year: targetYear,
days_in_month: new Date(targetYear, targetMonth, 0).getDate(),
first_day_of_week: calendar.firstDay,
calendar_grid: calendar.grid,
weekends: "Saturday and Sunday",
is_leap_year: (targetYear % 4 === 0 && targetYear % 100 !== 0) || (targetYear % 400 === 0)
};
} catch (error) {
return `Error generating calendar: ${error}`;
}
},
});
// Tool 3: Calculate date difference
const getDateDifferenceTool = tool({
name: "calculate_date_difference",
description: text`
Calculates the difference between two dates. Can calculate difference in days, months,
or years. Dates should be in YYYY-MM-DD format.
`,
parameters: {
date1: z.string().describe("First date in YYYY-MM-DD format"),
date2: z.string().describe("Second date in YYYY-MM-DD format"),
unit: z.enum(["days", "months", "years"]).optional().describe("Unit for difference calculation")
},
implementation: async ({ date1, date2, unit }) => {
try {
const d1 = new Date(date1);
const d2 = new Date(date2);
if (isNaN(d1.getTime()) || isNaN(d2.getTime())) {
return "Error: Invalid date format. Please use YYYY-MM-DD format.";
}
const diffMs = Math.abs(d2.getTime() - d1.getTime());
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const diffMonths = (d2.getFullYear() - d1.getFullYear()) * 12 + (d2.getMonth() - d1.getMonth());
const diffYears = d2.getFullYear() - d1.getFullYear();
return {
date1: date1,
date2: date2,
difference: {
days: Math.abs(diffDays),
months: Math.abs(diffMonths),
years: Math.abs(diffYears)
},
formatted: `${Math.abs(diffDays)} days, ${Math.abs(diffMonths)} months, ${Math.abs(diffYears)} years`
};
} catch (error) {
return `Error calculating date difference: ${error}`;
}
},
});
// Tool 4: Get holidays (simplified)
const getHolidaysTool = tool({
name: "get_holidays",
description: text`
Returns major holidays for a given month and year. Includes both fixed-date and
floating holidays (like Easter). Currently supports major international holidays.
`,
parameters: {
month: z.number().min(1).max(12).describe("Month (1-12)"),
year: z.number().optional().describe("Year"),
country: z.string().optional().describe("Country code (e.g., 'US', 'GB', 'RU')")
},
implementation: async ({ month, year }) => {
const targetYear = year || new Date().getFullYear();
const holidays = getHolidaysForMonth(month, targetYear);
return {
month: month,
year: targetYear,
holidays: holidays,
total_holidays: holidays.length
};
},
});
// Tool 5: Convert between timezones
const convertTimezoneTool = tool({
name: "convert_timezone",
description: text`
Converts a specific time from one timezone to another.
Time should be in 24-hour format (HH:MM).
`,
parameters: {
time: z.string().describe("Time in HH:MM format (24-hour)"),
from_timezone: z.string().describe("Source timezone"),
to_timezone: z.string().describe("Target timezone"),
date: z.string().optional().describe("Date in YYYY-MM-DD format (defaults to today)")
},
implementation: async ({ time, from_timezone, to_timezone, date }) => {
try {
const [hours, minutes] = time.split(":").map(Number);
const targetDate = date ? new Date(date) : new Date();
targetDate.setHours(hours, minutes, 0, 0);
const fromTime = new Date(targetDate.toLocaleString("en-US", { timeZone: from_timezone }));
const toTime = new Date(targetDate.toLocaleString("en-US", { timeZone: to_timezone }));
return {
original: {
time: time,
timezone: from_timezone,
datetime: fromTime.toISOString()
},
converted: {
time: `${String(toTime.getHours()).padStart(2, '0')}:${String(toTime.getMinutes()).padStart(2, '0')}`,
timezone: to_timezone,
datetime: toTime.toISOString()
}
};
} catch (error) {
return `Error converting timezone: ${error}`;
}
},
});
// Tool 6: Get system's local time
const getLocalTimeTool = tool({
name: "get_local_time",
description: text`
Gets the LOCAL system time from the user's computer — this is the time where the user
physically is. Use this tool FIRST for simple questions like "what time is it?",
"how late is it?", "what's the current time?", or "сколько времени?".
Only use get_current_time if the user specifically asks about a different timezone.
Returns detailed local time information including both 12h and 24h formats,
date, day of week, month, year, hours, minutes, seconds, and timezone details.
`,
parameters: {},
implementation: async () => {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
// 24h format
const time24h = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
// 12h format
const period = hours >= 12 ? "PM" : "AM";
const hours12 = hours % 12 || 12;
const time12h = `${hours12}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')} ${period}`;
return {
time: {
"24h": time24h,
"12h": time12h,
hours: hours,
minutes: minutes,
seconds: seconds
},
date: {
iso: now.toISOString().split('T')[0],
formatted: now.toLocaleDateString(),
day: now.getDate(),
month_number: now.getMonth() + 1,
month_name: now.toLocaleDateString('en-US', { month: 'long' }),
year: now.getFullYear(),
day_of_week: now.toLocaleDateString('en-US', { weekday: 'long' })
},
timezone: {
name: Intl.DateTimeFormat().resolvedOptions().timeZone,
offset_minutes: now.getTimezoneOffset(),
offset_hours: now.getTimezoneOffset() / -60
},
unix_timestamp: Math.floor(now.getTime() / 1000),
iso8601: now.toISOString()
};
},
});
return [
getLocalTimeTool,
getCurrentTimeTool,
getCalendarTool,
getDateDifferenceTool,
getHolidaysTool,
convertTimezoneTool
];
}
// Helper functions
function getWeekNumber(date: Date, timezone: string): number {
const targetDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
const startOfYear = new Date(targetDate.getFullYear(), 0, 1);
const days = Math.floor((targetDate.getTime() - startOfYear.getTime()) / (24 * 60 * 60 * 1000));
return Math.ceil((days + startOfYear.getDay() + 1) / 7);
}
function generateCalendar(year: number, month: number, timezone: string) {
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const daysInMonth = lastDay.getDate();
const startingDay = firstDay.getDay(); // 0 = Sunday
const grid: (number | null)[][] = [];
let currentWeek: (number | null)[] = [];
// Fill first week with null values before the first day
for (let i = 0; i < startingDay; i++) {
currentWeek.push(null);
}
// Fill in the days
for (let day = 1; day <= daysInMonth; day++) {
currentWeek.push(day);
if (currentWeek.length === 7) {
grid.push(currentWeek);
currentWeek = [];
}
}
// Fill remaining days of last week with null
if (currentWeek.length > 0) {
while (currentWeek.length < 7) {
currentWeek.push(null);
}
grid.push(currentWeek);
}
return {
firstDay: startingDay,
grid: grid,
totalWeeks: grid.length
};
}
function getHolidaysForMonth(month: number, year: number) {
const holidays: { date: string; name: string; type: string }[] = [];
const fixedHolidays: Record<number, { day: number; name: string }[]> = {
1: [
{ day: 1, name: "New Year's Day" },
{ day: 7, name: "Orthodox Christmas" }
],
2: [
{ day: 14, name: "Valentine's Day" }
],
3: [
{ day: 8, name: "International Women's Day" },
{ day: 17, name: "St. Patrick's Day" }
],
5: [
{ day: 1, name: "Labor Day" },
{ day: 9, name: "Victory Day" }
],
10: [
{ day: 31, name: "Halloween" }
],
12: [
{ day: 25, name: "Christmas Day" },
{ day: 31, name: "New Year's Eve" }
]
};
const monthHolidays = fixedHolidays[month] || [];
monthHolidays.forEach(holiday => {
holidays.push({
date: `${year}-${String(month).padStart(2, '0')}-${String(holiday.day).padStart(2, '0')}`,
name: holiday.name,
type: "fixed"
});
});
// Add Easter calculation (simplified)
if (month === 4 || month === 5) {
const easterDate = calculateEaster(year);
if (easterDate.getMonth() + 1 === month) {
holidays.push({
date: easterDate.toISOString().split('T')[0],
name: "Easter Sunday",
type: "floating"
});
}
}
return holidays;
}
function calculateEaster(year: number): Date {
// Gauss's Easter algorithm
const a = year % 19;
const b = Math.floor(year / 100);
const c = year % 100;
const d = Math.floor(b / 4);
const e = b % 4;
const f = Math.floor((b + 8) / 25);
const g = Math.floor((b - f + 1) / 3);
const h = (19 * a + b - d - g + 15) % 30;
const i = Math.floor(c / 4);
const k = c % 4;
const l = (32 + 2 * e + 2 * i - h - k) % 7;
const m = Math.floor((a + 11 * h + 22 * l) / 451);
const month = Math.floor((h + l - 7 * m + 114) / 31);
const day = ((h + l - 7 * m + 114) % 31) + 1;
return new Date(year, month - 1, day);
}