EcoTracker-II / backend /src /services /aiService.js
Akash-Dragon's picture
refactor: restructure project as a monorepo and implement backend API
7e49c4d
Raw
History Blame Contribute Delete
10.1 kB
import Groq from 'groq-sdk';
import logger from '../utils/logger.js';
const apiKey = process.env.GROQ_API_KEY;
let groq = null;
if (apiKey) {
try {
groq = new Groq({ apiKey });
logger.info('✅ Groq SDK initialized successfully.');
} catch (err) {
logger.error('❌ Failed to initialize Groq SDK:', { error: err.message });
}
} else {
logger.warn('⚠️ No GROQ_API_KEY found. AI endpoints will run in Mock Fallback mode.');
}
// Simple in-memory cache for the daily eco tip
let dailyTipCache = {
date: null,
tip: null,
};
export const aiService = {
/**
* Conversational Eco-Coach Chatbot
*/
chat: async (message, context = {}) => {
if (!groq) {
return {
reply: `Hi! I'm your CarbonSense Eco Coach. Currently, I'm running in offline demonstration mode. To unlock my full AI intelligence capabilities, please add a valid \`GROQ_API_KEY\` to your backend environment settings.\n\nBased on your message ("${message}"), my recommendation is to reduce daily car travel and review electricity usage!`,
suggestedActions: [
'Turn off unused appliances',
'Consider public transport',
'Explore green electricity tariffs',
],
};
}
try {
const systemPrompt = `You are CarbonSense Ocean Intelligence Coach, an expert sustainability advisor.
You are helping a user reduce their carbon footprint.
Be encouraging, professional, and practical. Use bullet points for recommendations.
Keep answers concise (max 3 short paragraphs).
${
context.footprint
? `Current User Footprint Context:
- Total Annual Footprint: ${context.footprint} kg CO2e
- Breakdown: Transport: ${context.breakdown?.transport || 0}%, Electricity: ${context.breakdown?.electricity || 0}%, Food: ${context.breakdown?.food || 0}%, Shopping: ${context.breakdown?.shopping || 0}%`
: ''
}`;
const chatCompletion = await groq.chat.completions.create({
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: message },
],
model: 'llama-3.1-8b-instant',
temperature: 0.7,
max_tokens: 350,
});
const reply = chatCompletion.choices[0]?.message?.content || 'No response generated.';
return {
reply,
suggestedActions: [
'Calculate footprint again',
'Optimize my energy contract',
'Find cyclable commuting routes',
],
};
} catch (err) {
logger.error('Groq Chat completion failed:', { error: err.message || err });
return {
reply:
'I apologize, but my connection to the AI engine timed out. Please try again in a few moments!',
suggestedActions: [],
};
}
},
/**
* Personalized 30-Day Net Zero Action Plan
*/
generatePlan: async (footprint, breakdown, goal = '20% reduction') => {
if (!groq) {
return {
plan: [
{
week: 1,
actions: [
'Switch to 100% renewable electricity tariff',
'Swap one meat meal daily for a plant-based alternative',
],
expectedReduction: 'Approx. 50 kg',
},
{
week: 2,
actions: [
'Walk or cycle for all trips under 3 km',
'Unplug standby electronics when sleeping',
],
expectedReduction: 'Approx. 30 kg',
},
{
week: 3,
actions: [
'Wash clothes at 30°C and line-dry instead of tumble drying',
'Avoid purchasing fast-fashion clothing items',
],
expectedReduction: 'Approx. 40 kg',
},
{
week: 4,
actions: [
'Replace three short regional drives with public transit',
'Conduct a home energy audit',
],
expectedReduction: 'Approx. 60 kg',
},
],
goal,
};
}
try {
const prompt = `Generate a structured, personalized 30-day net-zero action plan for a user to achieve their goal of "${goal}".
Annual Footprint: ${footprint} kg CO2e.
Category breakdown: Transport: ${breakdown.transport || 0}%, Energy: ${breakdown.energy || 0}%, Food: ${breakdown.food || 0}%, Shopping: ${breakdown.shopping || 0}%.
Return ONLY a valid JSON object matching this structure:
{
"plan": [
{
"week": 1,
"actions": ["action 1", "action 2"],
"expectedReduction": "X kg"
},
...
]
}
Ensure it is valid parseable JSON. Do not include markdown code block syntax in your response, just raw JSON.`;
const chatCompletion = await groq.chat.completions.create({
messages: [{ role: 'user', content: prompt }],
model: 'llama-3.1-8b-instant',
response_format: { type: 'json_object' },
temperature: 0.5,
max_tokens: 500,
});
return JSON.parse(chatCompletion.choices[0]?.message?.content || '{}');
} catch (err) {
logger.error('Groq Plan generation failed:', { error: err.message || err });
return { plan: [], error: 'Failed to generate plan.' };
}
},
/**
* Daily Eco Tip (Cached 24h)
*/
getDailyTip: async () => {
const today = new Date().toISOString().split('T')[0];
if (dailyTipCache.date === today && dailyTipCache.tip) {
return dailyTipCache.tip;
}
const defaultTip = {
tip: 'Wash laundry in cold water (30°C or below). This saves up to 75-90% of the energy used in a cycle, which goes solely toward heating the water!',
category: 'energy',
impact: 'Up to 75kg CO2/year saved',
icon: 'local-laundry-service',
};
if (!groq) {
dailyTipCache = { date: today, tip: defaultTip };
return defaultTip;
}
try {
const prompt = `Generate a practical, fresh daily eco tip for carbon footprint reduction.
Return ONLY a valid JSON object matching this structure:
{
"tip": "Tip explanation goes here...",
"category": "energy" | "transport" | "food" | "shopping",
"impact": "X kg CO2 saved per year",
"icon": "icon-identifier-string"
}`;
const chatCompletion = await groq.chat.completions.create({
messages: [{ role: 'user', content: prompt }],
model: 'llama-3.1-8b-instant',
response_format: { type: 'json_object' },
temperature: 0.9, // Higher temperature for variety
max_tokens: 150,
});
const parsedTip = JSON.parse(chatCompletion.choices[0]?.message?.content || '{}');
dailyTipCache = { date: today, tip: parsedTip };
return parsedTip;
} catch (err) {
logger.error('Groq Daily Tip generation failed:', { error: err.message || err });
return defaultTip;
}
},
/**
* Carbon Intelligence Score & Feedback
*/
getScoreFeedback: async (score, breakdown) => {
if (!groq) {
return {
score,
grade: score >= 80 ? 'A' : score >= 60 ? 'B' : score >= 40 ? 'C' : 'D',
feedback: `You scored ${score}/100. Focus on your largest footprint contributor. Reducing meat intake and switching to green electricity tariffs are the highest-yield steps you can take.`,
};
}
try {
const prompt = `Provide carbon intelligence feedback on a sustainability score of ${score}/100.
Breakdown: Transport: ${breakdown.transport || 0}%, Energy: ${breakdown.energy || 0}%, Food: ${breakdown.food || 0}%, Shopping: ${breakdown.shopping || 0}%.
Return ONLY a valid JSON object matching this structure:
{
"score": ${score},
"grade": "A" | "B" | "C" | "D" | "F",
"feedback": "A short, actionable paragraph of feedback summarizing their emissions profiles."
}`;
const chatCompletion = await groq.chat.completions.create({
messages: [{ role: 'user', content: prompt }],
model: 'llama-3.1-8b-instant',
response_format: { type: 'json_object' },
temperature: 0.5,
max_tokens: 250,
});
return JSON.parse(chatCompletion.choices[0]?.message?.content || '{}');
} catch (err) {
logger.error('Groq Score Feedback failed:', { error: err.message || err });
return {
score,
grade: 'B',
feedback: 'Your emissions are moderate. Review transport and food habits.',
};
}
},
/**
* Narrative Comparison
*/
getCompareNarrative: async (totalEmission, breakdown) => {
if (!groq) {
return {
vsGlobal: totalEmission > 4700 ? 'higher' : 'lower',
standoutCategory: 'transport',
narrative: `Your annual footprint of ${(totalEmission / 1000).toFixed(1)} tonnes is ${totalEmission > 4700 ? 'above' : 'below'} the global average of 4.7 tonnes. Focus on choosing green modes of transport and reducing consumer electronics purchases to further lower your footprint.`,
};
}
try {
const prompt = `Compare a user's carbon footprint of ${totalEmission} kg CO2e against the global average (4,700 kg/year).
Breakdown: Transport: ${breakdown.transport || 0}%, Energy: ${breakdown.energy || 0}%, Food: ${breakdown.food || 0}%, Shopping: ${breakdown.shopping || 0}%.
Return ONLY a valid JSON object matching this structure:
{
"vsGlobal": "higher" | "lower",
"standoutCategory": "transport" | "energy" | "food" | "shopping",
"narrative": "A concise paragraph (2-3 sentences) evaluating their standing and highlighting which category has the highest relative impact."
}`;
const chatCompletion = await groq.chat.completions.create({
messages: [{ role: 'user', content: prompt }],
model: 'llama-3.1-8b-instant',
response_format: { type: 'json_object' },
temperature: 0.6,
max_tokens: 250,
});
return JSON.parse(chatCompletion.choices[0]?.message?.content || '{}');
} catch (err) {
logger.error('Groq Comparison Narrative failed:', { error: err.message || err });
return {
vsGlobal: totalEmission > 4700 ? 'higher' : 'lower',
standoutCategory: 'transport',
narrative: 'Comparison computed successfully.',
};
}
},
};