Spaces:
Running
Running
File size: 6,230 Bytes
899a91f 5f07a23 aba9d46 5f07a23 899a91f 5f07a23 aba9d46 899a91f aba9d46 5f07a23 aba9d46 5f07a23 aba9d46 899a91f aba9d46 899a91f aba9d46 899a91f aba9d46 5f07a23 aba9d46 899a91f aba9d46 5f07a23 aba9d46 899a91f aba9d46 5f07a23 899a91f aba9d46 899a91f aba9d46 5f07a23 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
import { GoogleGenerativeAI } from "@google/generative-ai";
import { NextResponse } from 'next/server';
// Configuration for the API route
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb', // Increase limit to 10MB (adjust if needed)
},
},
};
export default async function handler(req, res) {
// Only allow POST requests
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
// Add retry configuration
const MAX_RETRIES = 2;
const RETRY_DELAY = 1000; // 1 second
let retryCount = 0;
// Function to wait for a delay
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
try {
const { imageData, customApiKey } = req.body;
// Validate inputs
if (!imageData) {
return res.status(400).json({ error: 'Image data is required' });
}
// Set up the API key
const apiKey = customApiKey || process.env.GEMINI_API_KEY;
if (!apiKey) {
console.error('Missing Gemini API key');
return res.status(500).json({ error: 'API key is not configured' });
}
// Retry loop for handling transient errors
while (true) {
try {
console.log(`Initializing Gemini AI for doodle conversion (attempt ${retryCount + 1}/${MAX_RETRIES + 1})`);
// Initialize the Gemini API
const genAI = new GoogleGenerativeAI(apiKey);
console.log('Configuring Gemini model');
const model = genAI.getGenerativeModel({
model: "gemini-2.0-flash-exp-image-generation",
generationConfig: {
temperature: 1,
topP: 0.95,
topK: 40,
maxOutputTokens: 8192,
responseModalities: ["image", "text"]
}
});
// Create the prompt for doodle conversion
const prompt = `Could you please convert this image into a black and white doodle.
Requirements:
- Use ONLY pure black lines on a pure white background
- No gray tones or shading
- Maintain the key shapes and outlines
- Follow the original content but simplify if needed
- IMPORTANT: If this image contains any text, logo, or wordmark:
* Simply convert it to black and white, and pass it through
* Preserve ALL text exactly as it appears in the original
* Maintain the exact spelling, letterspacing, and arrangement of letters
* Keep text legible and clear with high contrast
* Do not simplify or omit any text elements
* Text should remain readable in the final doodle, and true to the original :))`;
// Prepare the generation content
console.log('Including image data in generation request');
const generationContent = [
{
inlineData: {
data: imageData,
mimeType: "image/png"
}
},
{ text: prompt }
];
// Generate content
console.log(`Calling Gemini API for doodle conversion (attempt ${retryCount + 1}/${MAX_RETRIES + 1})...`);
const result = await model.generateContent(generationContent);
console.log('Gemini API response received');
const response = await result.response;
// Process the response to extract image data
let convertedImageData = null;
if (!response?.candidates?.[0]?.content?.parts) {
console.error('Invalid response structure:', response);
throw new Error('Invalid response structure from Gemini API');
}
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
convertedImageData = part.inlineData.data;
console.log('Found image data in response');
break;
}
}
if (!convertedImageData) {
console.error('No image data in response parts:', response.candidates[0].content.parts);
throw new Error('No image data found in response parts');
}
// Return the converted image data
console.log('Successfully generated doodle');
return res.status(200).json({
success: true,
imageData: convertedImageData
});
} catch (attemptError) {
// Only retry certain types of errors that might be transient
const isRetryableError =
attemptError.message?.includes('timeout') ||
attemptError.message?.includes('network') ||
attemptError.message?.includes('ECONNRESET') ||
attemptError.message?.includes('rate limit') ||
attemptError.message?.includes('503') ||
attemptError.message?.includes('500') ||
attemptError.message?.includes('overloaded') ||
attemptError.message?.includes('connection');
// Check if we should retry
if (retryCount < MAX_RETRIES && isRetryableError) {
console.log(`Retryable error encountered (${retryCount + 1}/${MAX_RETRIES}):`, attemptError.message);
retryCount++;
// Wait before retrying
await wait(RETRY_DELAY * retryCount);
continue;
}
// If we've exhausted retries or it's not a retryable error, rethrow
throw attemptError;
}
}
} catch (error) {
console.error('Error in /api/convert-to-doodle:', error);
// Check for specific error types
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.'
});
}
if (error.message?.includes('API key')) {
return res.status(500).json({
error: 'Invalid or missing API key. Please check your configuration.'
});
}
if (error.message?.includes('safety') || error.message?.includes('blocked') || error.message?.includes('harmful')) {
return res.status(400).json({
error: 'Content was blocked due to safety filters. Try a different prompt.'
});
}
return res.status(500).json({
error: error.message || 'An error occurred during conversion.',
details: error.stack,
retries: retryCount
});
}
} |