198
services/geminiService.ts
Normal file
198
services/geminiService.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { GoogleGenAI, Type } from "@google/genai";
|
||||
import { EventCategory, CalendarEvent, AppSettings } from "../types";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const apiKey = process.env.API_KEY || '';
|
||||
const ai = new GoogleGenAI({ apiKey });
|
||||
|
||||
// --- Event Parsing ---
|
||||
export const parseNaturalLanguageEvent = async (
|
||||
input: string,
|
||||
referenceDate: Date = new Date()
|
||||
): Promise<{ title: string; startTime: string; endTime: string; description?: string; category: EventCategory } | null> => {
|
||||
if (!apiKey) {
|
||||
console.warn("No API Key found for Gemini");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const model = 'gemini-3-flash-preview';
|
||||
const prompt = `
|
||||
Current Date context: ${referenceDate.toISOString()}
|
||||
User Input: "${input}"
|
||||
|
||||
Extract event details.
|
||||
Categorize into one of: 'general', 'chore', 'date', 'work', 'fun'.
|
||||
If it sounds romantic, it's a date. If it's cleaning/shopping, it's a chore.
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model,
|
||||
contents: prompt,
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
title: { type: Type.STRING },
|
||||
startTime: { type: Type.STRING, description: "ISO 8601 format date string" },
|
||||
endTime: { type: Type.STRING, description: "ISO 8601 format date string" },
|
||||
description: { type: Type.STRING },
|
||||
category: { type: Type.STRING, enum: ['general', 'chore', 'date', 'work', 'fun'] }
|
||||
},
|
||||
required: ["title", "startTime", "endTime", "category"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const text = response.text;
|
||||
if (!text) return null;
|
||||
return JSON.parse(text);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error parsing event with Gemini:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Chat & Assistant ---
|
||||
export const generateChatResponse = async (
|
||||
input: string,
|
||||
settings: AppSettings,
|
||||
userRole: string
|
||||
): Promise<{ text: string; event?: CalendarEvent }> => {
|
||||
if (!apiKey) return { text: "I need a Gemini API Key to function properly." };
|
||||
|
||||
try {
|
||||
const model = 'gemini-3-flash-preview';
|
||||
const systemPrompt = `
|
||||
You are a mystical calendar oracle for a couple.
|
||||
Current Date: ${new Date().toISOString()}
|
||||
User Role: ${userRole}
|
||||
Mom's Sign: ${settings.momZodiac}
|
||||
Dad's Sign: ${settings.dadZodiac}
|
||||
|
||||
If the user asks to create an event, output JSON with "isEvent": true.
|
||||
Otherwise, output JSON with "isEvent": false and a "message".
|
||||
Keep messages bubbly, mystical, and concise.
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model,
|
||||
contents: `${systemPrompt}\n\nUser: ${input}`,
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
isEvent: { type: Type.BOOLEAN },
|
||||
message: { type: Type.STRING },
|
||||
title: { type: Type.STRING },
|
||||
description: { type: Type.STRING },
|
||||
startTime: { type: Type.STRING },
|
||||
endTime: { type: Type.STRING },
|
||||
category: { type: Type.STRING, enum: ['general', 'chore', 'date', 'work', 'fun', 'magic'] }
|
||||
},
|
||||
required: ["isEvent"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(response.text || "{}");
|
||||
|
||||
if (parsed.isEvent) {
|
||||
const newEvent: CalendarEvent = {
|
||||
id: uuidv4(),
|
||||
title: parsed.title || "New Event",
|
||||
description: parsed.description || "",
|
||||
startTime: parsed.startTime || new Date().toISOString(),
|
||||
endTime: parsed.endTime || new Date().toISOString(),
|
||||
category: (parsed.category as EventCategory) || 'general',
|
||||
createdBy: userRole as any
|
||||
};
|
||||
return { text: parsed.message || `I've scheduled "${parsed.title}" for you!`, event: newEvent };
|
||||
} else {
|
||||
return { text: parsed.message || "The stars are silent." };
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Gemini Chat Error", error);
|
||||
return { text: "The cosmic connection is weak right now." };
|
||||
}
|
||||
};
|
||||
|
||||
// --- Daily Horoscope ---
|
||||
export const generateDailyHoroscope = async (
|
||||
momSign: string,
|
||||
dadSign: string,
|
||||
settings: AppSettings
|
||||
): Promise<string> => {
|
||||
if (!apiKey) return "Magic requires an API Key.";
|
||||
|
||||
try {
|
||||
const model = 'gemini-3-flash-preview';
|
||||
const prompt = `
|
||||
Generate a very short, mystical horoscope prediction (max 10 words) for a couple with signs ${momSign} and ${dadSign} for today.
|
||||
Focus on their shared energy, love, or domestic life.
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model,
|
||||
contents: prompt,
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
prediction: { type: Type.STRING }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(response.text || "{}");
|
||||
return parsed.prediction || "Cosmic energy flows through your home.";
|
||||
} catch (e) {
|
||||
return "The stars are aligning.";
|
||||
}
|
||||
};
|
||||
|
||||
// --- Schedule Suggestions ---
|
||||
export const analyzeScheduleAndSuggest = async (
|
||||
events: CalendarEvent[],
|
||||
settings: AppSettings
|
||||
): Promise<string[]> => {
|
||||
if (!apiKey) return ["Plan a surprise dinner!", "Go for a moonlit walk.", "Cook a meal together."];
|
||||
|
||||
try {
|
||||
const model = 'gemini-3-flash-preview';
|
||||
const eventsSummary = events.slice(0, 20).map(e => // Limit to 20 for context window efficiency
|
||||
`- ${e.title} (${e.category}) on ${e.startTime}`
|
||||
).join('\n');
|
||||
|
||||
const prompt = `
|
||||
Analyze this couple's schedule:
|
||||
${eventsSummary}
|
||||
|
||||
Based on these events, suggest 3 specific, thoughtful, or fun additional events they should add to balance their life.
|
||||
Look for missing date nights, overwhelming work blocks, or chores that need doing.
|
||||
Return 3 short strings.
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model,
|
||||
contents: prompt,
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: {
|
||||
type: Type.ARRAY,
|
||||
items: { type: Type.STRING }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return JSON.parse(response.text || "[]");
|
||||
} catch (e) {
|
||||
return ["Plan a date night", "Take a rest day", "Organize the pantry"];
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user