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 || "이 이미지를 보고 (보일 수도 있는 손이나 손가락은 무시한 채) 오로지 playing cards만 식별하세요. 식별된 카드의 정확한 정보만 출력하세요(예, A 스페이드, 10 하트, J 다이아몬드"; // 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: "🤔" }); } }