feat: use LLM to autonomously parse text itineraries into scheduled reminders
This commit is contained in:
parent
aab9e60100
commit
294f10698e
|
|
@ -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) {
|
||||
return new Promise((resolve) => {
|
||||
const body = JSON.stringify({ to, message });
|
||||
|
|
@ -155,6 +242,15 @@ async function generateAndSendBriefing(trip, dayPlan) {
|
|||
for (const group of groupsToNotify) {
|
||||
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 ───────────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in New Issue