feat: use LLM to autonomously parse text itineraries into scheduled reminders

This commit is contained in:
Antigravity 2026-05-29 17:19:53 -04:00
parent aab9e60100
commit 294f10698e
1 changed files with 96 additions and 0 deletions

View File

@ -109,6 +109,93 @@ async function getAiInsight(activityDetails, location, modelName = 'qwen2.5:0.5b
}); });
} }
async function generateAiSchedule(activities, meals, date, trip) {
return new Promise((resolve) => {
const prompt = `You are a strict JSON API scheduling agent. Extract a daily schedule from these plans:
Activities: ${activities}
Meals: ${meals}
Date: ${date}
Return ONLY a JSON array of objects. Each object must have "time" (in 24-hour HH:mm format) and "message" (a short, fun 1-sentence reminder). Estimate reasonable times if none are given (e.g. breakfast 8:00, lunch 12:30, dinner 18:30). DO NOT output any markdown or conversational text.`;
const modelName = trip.llmModel || 'qwen2.5:0.5b';
// Support future providers via trip.llmProvider
// Defaulting to Ollama for now
const body = JSON.stringify({ model: modelName, prompt, stream: false });
const url = new URL(OLLAMA_URL);
const req = http.request({
hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
}, (res) => {
let d = '';
res.on('data', c => d += c);
res.on('end', () => {
try {
const raw = JSON.parse(d).response;
// Extract JSON array using regex in case of conversational wrapper
const match = raw.match(/\[[\s\S]*\]/);
if (match) {
resolve(JSON.parse(match[0]));
} else {
resolve([]);
}
} catch (e) {
console.error('[TripService] AI Schedule parsing failed:', e.message);
resolve([]);
}
});
});
req.on('error', () => resolve([]));
req.write(body);
req.end();
});
}
function scheduleRemindersBatch(events, trip, dateStr) {
if (!events || events.length === 0) return;
// Base date object for today in Eastern Time (approximated locally)
const baseDate = new Date(dateStr + "T00:00:00-04:00");
const items = events.map(ev => {
const [hh, mm] = ev.time.split(':');
const eventTime = new Date(baseDate);
eventTime.setHours(parseInt(hh, 10));
eventTime.setMinutes(parseInt(mm, 10));
// Send reminder 15 minutes before the event time
const sendAt = new Date(eventTime.getTime() - 15 * 60000);
// Don't schedule in the past
if (sendAt <= new Date()) return null;
return {
message: `⏰ *${trip.name} Schedule Alert*\n\n${ev.message}\n\n_Starts at ${ev.time}_`,
sendAt: sendAt.toISOString(),
label: `${trip.name} - ${ev.message.slice(0, 30)}...`,
to: trip.sendToGroup && trip.groupUrls && trip.groupUrls.length > 0 ? trip.groupUrls[0] : (GROUP_URL || PERSONAL_NUM)
};
}).filter(Boolean);
if (items.length === 0) return;
const body = JSON.stringify({ items });
const req = http.request({
hostname: 'reminder-service.ai-agents.svc.cluster.local',
port: 6001,
path: '/reminders/batch',
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
}, (res) => {
res.on('data', () => {});
res.on('end', () => console.log(`[TripService] Batch queued ${items.length} reminders via reminder-service`));
});
req.on('error', (e) => console.error('[TripService] Failed to queue reminders:', e.message));
req.write(body);
req.end();
}
function sendWhatsApp(to, message) { function sendWhatsApp(to, message) {
return new Promise((resolve) => { return new Promise((resolve) => {
const body = JSON.stringify({ to, message }); const body = JSON.stringify({ to, message });
@ -155,6 +242,15 @@ async function generateAndSendBriefing(trip, dayPlan) {
for (const group of groupsToNotify) { for (const group of groupsToNotify) {
await sendWhatsApp(group, message); await sendWhatsApp(group, message);
} }
// Generate and schedule detailed AI itinerary
console.log(`[TripService] Generating detailed AI schedule for ${trip.name} - ${dayPlan.date}`);
const scheduleEvents = await generateAiSchedule(dayPlan.activities, dayPlan.meals, dayPlan.date, trip);
if (scheduleEvents.length > 0) {
scheduleRemindersBatch(scheduleEvents, trip, dayPlan.date);
} else {
console.log(`[TripService] AI yielded no parseable schedule events.`);
}
} }
// ── Routes ─────────────────────────────────────────────────────────────────── // ── Routes ───────────────────────────────────────────────────────────────────