import { ProposedEventChange, getDay, Day, DAY_TO_GERMAN, } from "@caldav/shared"; import { AIContext } from "../../services/interfaces"; import { formatDate, formatTime, formatDateTime } from "./eventFormatter"; /** * Result of executing a tool call. */ export interface ToolResult { content: string; proposedChange?: ProposedEventChange; } /** * Execute a tool call and return the result. * This function is provider-agnostic and can be used with any LLM. */ export function executeToolCall( name: string, args: Record, context: AIContext, ): ToolResult { switch (name) { case "getDay": { const date = getDay( args.day as Day, args.offset as number, args.hour as number, args.minute as number, ); const dayName = DAY_TO_GERMAN[args.day as Day]; return { content: `${date.toISOString()} (${dayName}, ${formatDate(date)} um ${formatTime(date)} Uhr)`, }; } case "getCurrentDateTime": { const now = context.currentDate; return { content: `${now.toISOString()} (${formatDateTime(now)})`, }; } case "proposeCreateEvent": { const event = { title: args.title as string, startTime: new Date(args.startTime as string), endTime: new Date(args.endTime as string), description: args.description as string | undefined, isRecurring: args.isRecurring as boolean | undefined, recurrenceRule: args.recurrenceRule as string | undefined, }; const dateStr = formatDate(event.startTime); const startStr = formatTime(event.startTime); const endStr = formatTime(event.endTime); return { content: `Event-Vorschlag erstellt: "${event.title}" am ${dateStr} von ${startStr} bis ${endStr} Uhr`, proposedChange: { action: "create", event, }, }; } case "proposeUpdateEvent": { const eventId = args.eventId as string; const existingEvent = context.existingEvents.find( (e) => e.id === eventId, ); if (!existingEvent) { return { content: `Event mit ID ${eventId} nicht gefunden.` }; } const updates: Record = {}; if (args.title) updates.title = args.title; if (args.startTime) updates.startTime = new Date(args.startTime as string); if (args.endTime) updates.endTime = new Date(args.endTime as string); if (args.description) updates.description = args.description; // Build event object for display (merge existing with updates) const displayEvent = { title: (updates.title as string) || existingEvent.title, startTime: (updates.startTime as Date) || existingEvent.startTime, endTime: (updates.endTime as Date) || existingEvent.endTime, description: (updates.description as string) || existingEvent.description, isRecurring: existingEvent.isRecurring, }; return { content: `Update-Vorschlag für "${existingEvent.title}" erstellt.`, proposedChange: { action: "update", eventId, updates, event: displayEvent, }, }; } case "proposeDeleteEvent": { const eventId = args.eventId as string; const existingEvent = context.existingEvents.find( (e) => e.id === eventId, ); if (!existingEvent) { return { content: `Event mit ID ${eventId} nicht gefunden.` }; } return { content: `Lösch-Vorschlag für "${existingEvent.title}" erstellt.`, proposedChange: { action: "delete", eventId, event: { title: existingEvent.title, startTime: existingEvent.startTime, endTime: existingEvent.endTime, description: existingEvent.description, isRecurring: existingEvent.isRecurring, }, }, }; } case "searchEvents": { const query = (args.query as string).toLowerCase(); const matches = context.existingEvents.filter((e) => e.title.toLowerCase().includes(query), ); if (matches.length === 0) { return { content: `Keine Termine mit "${args.query}" gefunden.` }; } const results = matches .map((e) => { const start = new Date(e.startTime); return `- ${e.title} (ID: ${e.id}) am ${formatDate(start)} um ${formatTime(start)} Uhr`; }) .join("\n"); return { content: `Gefundene Termine:\n${results}` }; } default: return { content: `Unbekannte Funktion: ${name}` }; } }