import { GoogleGenerativeAI } from '@google/generative-ai'; // Initialize Gemini API with error handling const initializeGeminiAI = () => { const apiKey = process.env.GEMINI_API_KEY; if (!apiKey) { throw new Error('GEMINI_API_KEY is not set in environment variables'); } return new GoogleGenerativeAI(apiKey); }; // Configure API route options export const config = { api: { bodyParser: { sizeLimit: '10mb' // Increase the body size limit to 10MB for larger images } } }; export default async function handler(req, res) { // Only allow POST requests if (req.method !== 'POST') { res.setHeader('Allow', ['POST']); return res.status(405).json({ error: `Method ${req.method} Not Allowed` }); } try { const { image, customApiKey } = req.body; if (!image) { return res.status(400).json({ error: 'Image is required' }); } // Extract base64 data const base64Data = image.split(',')[1]; if (!base64Data) { return res.status(400).json({ error: 'Invalid image format' }); } // Initialize Gemini with the appropriate API key const apiKey = customApiKey || process.env.GEMINI_API_KEY; const genAI = new GoogleGenerativeAI(apiKey); const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' }); // Prepare the image parts const imageParts = [ { inlineData: { data: base64Data, mimeType: "image/jpeg" } } ]; // Create the prompt - Updated for clarity and better results const prompt = `Analyze this reference image/moodboard and create a detailed material prompt for a 3D rendering that captures its essence and visual qualities. The prompt should follow this format and style, but be unique and creative: Example 1: "Recreate this doodle as a physical chrome sculpture made of chromium metal tubes or pipes in a professional studio setting. If it is typography, render it accordingly, but always have a black background and studio lighting. Render it in Cinema 4D with Octane, using studio lighting against a pure black background. Make it look like a high-end product rendering of a sculptural piece." Example 2: "Convert this drawing / text into a soft body physics render. Render it as if made of a soft, jelly-like material that responds to gravity and motion. Add realistic deformation, bounce, and squash effects typical of soft body dynamics. Use dramatic lighting against a black background to emphasize the material's translucency and surface properties. Make it look like a high-end 3D animation frame" Create a new, unique prompt based on the visual qualities of the uploaded image that follows a similar style but is completely different from the examples. Focus on: 1. Material properties (metallic, glass, fabric, liquid, etc.) 2. Lighting and environment (studio, dramatic, moody, etc.) 3. Visual style (high-end, realistic, stylized, etc.) 4. Rendering technique (Cinema 4D, Octane, etc.) Also suggest a short, memorable name for this material style (1-2 words) based on the key visual characteristics. Format the response as JSON with 'prompt' and 'suggestedName' fields.`; console.log('Calling Gemini Vision API for image analysis...'); // Generate content const result = await model.generateContent([prompt, ...imageParts]); const response = result.response; const responseText = response.text(); console.log('Received response from Gemini'); // Try to parse as JSON, fallback to text if needed try { // First try direct parse const jsonResponse = JSON.parse(responseText); return res.status(200).json(jsonResponse); } catch (e) { console.log('Response not in JSON format, attempting to extract JSON'); // Try to extract JSON from text response try { const jsonMatch = responseText.match(/\{[\s\S]*\}/); if (jsonMatch) { const extractedJson = JSON.parse(jsonMatch[0]); return res.status(200).json(extractedJson); } } catch (extractError) { console.error('Failed to extract JSON from response:', extractError); } // If all parsing attempts fail, create structured response from text const lines = responseText.split('\n'); let suggestedName = 'Custom Material'; // Try to find a name in the response for (const line of lines) { if (line.toLowerCase().includes('name:') || line.toLowerCase().includes('suggested name:')) { const namePart = line.split(':')[1]; if (namePart && namePart.trim()) { suggestedName = namePart.trim(); // Remove quotes if present suggestedName = suggestedName.replace(/^["'](.*)["']$/, '$1'); break; } } } return res.status(200).json({ prompt: responseText, suggestedName }); } } catch (error) { console.error('Error in analyze-image:', error); // Check for quota exceeded errors if (error.message?.includes('quota') || error.message?.includes('Resource has been exhausted')) { return res.status(429).json({ error: 'API quota exceeded. Please try again later or use your own API key.' }); } return res.status(500).json({ error: 'Failed to analyze image', details: error.message }); } }