import { GoogleGenerativeAI } from "@google/generative-ai"; // Initialize the Gemini API with the API key const apiKey = process.env.GEMINI_API_KEY; const genAI = new GoogleGenerativeAI(apiKey); // Maximum number of retries for API calls const MAX_RETRIES = 3; // Delay between retries (in milliseconds) const RETRY_DELAY = 1000; // Helper function to wait between retries const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { const { image, prompt } = req.body; if (!image) { return res.status(400).json({ error: 'Image data is required' }); } // Remove the data URL prefix const base64Image = image.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''); // Initialize the Gemini 2.0 Flash model const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); // Use the custom prompt if provided, otherwise use the default const promptText = prompt || "Look at this image and identify the main object or scene (ignoring any hands or fingers that might be visible). Generate a single, short, insightful thought (maximum 5 words) about this object or scene. Focus on something interesting, philosophical, or unexpected about it. DO NOT mention hands, fingers, pose estimation, motion tracking, or any computer vision technology in your response. Respond with just the thought, no additional text."; // Prepare the image part for the model const imagePart = { inlineData: { data: base64Image, mimeType: "image/jpeg", }, }; // Implement retry logic with exponential backoff let lastError = null; for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { try { // Generate content with the image const result = await model.generateContent([promptText, imagePart]); const response = await result.response; const text = response.text(); return res.status(200).json({ thought: text }); } catch (error) { console.error(`Attempt ${attempt + 1} failed:`, error.message); lastError = error; // Check if error is related to throttling or service unavailability if (error.message.includes('503 Service Unavailable') || error.message.includes('THROTTLED') || error.message.includes('overloaded')) { // Wait before retrying with exponential backoff await sleep(RETRY_DELAY * Math.pow(2, attempt)); continue; } else { // For other errors, don't retry break; } } } // If we've exhausted all retries or encountered a non-retryable error console.error('All attempts failed or non-retryable error:', lastError); // Provide a user-friendly error message based on the error type if (lastError.message.includes('THROTTLED') || lastError.message.includes('overloaded')) { return res.status(503).json({ error: 'The AI service is currently busy. Please try again in a moment.', fallbackThought: "🤔" }); } else if (lastError.message.includes('quota')) { return res.status(429).json({ error: 'API quota exceeded. Please try again later.', fallbackThought: "🤔" }); } else { return res.status(500).json({ error: 'Something went wrong while analyzing your image.', fallbackThought: "🤔" }); } } catch (error) { console.error('Unexpected error:', error); return res.status(500).json({ error: 'An unexpected error occurred', fallbackThought: "🤔" }); } }