Spaces:
Running
Running
File size: 6,750 Bytes
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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
import { GoogleGenerativeAI } from "@google/generative-ai";
// Configure API route options
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb' // Increase the body size limit to 10MB
}
}
};
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 { prompt, drawingData, customApiKey, generateTextOnly } = req.body;
// Validate prompt
if (!prompt) {
console.error('Missing prompt in request');
return res.status(400).json({ error: 'Prompt is required' });
}
// Log the API request details (excluding the full drawing data for brevity)
console.log('API Request:', {
prompt: prompt.substring(0, 100) + '...',
hasDrawingData: !!drawingData,
hasCustomApiKey: !!customApiKey,
generateTextOnly: !!generateTextOnly,
retryCount
});
// Check 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 with API key (attempt ${retryCount + 1}/${MAX_RETRIES + 1})`);
const genAI = new GoogleGenerativeAI(apiKey);
// Configure the model with settings based on the request type
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: generateTextOnly ? ["text"] : ["image", "text"]
}
});
// Handle text-only generation
if (generateTextOnly) {
console.log('Generating text-only response');
// Make text-only API call
const result = await model.generateContent(prompt);
const response = await result.response;
// Extract text from response
const textResponse = response.text();
console.log('Generated text response:', textResponse.substring(0, 100) + '...');
return res.status(200).json({
success: true,
textResponse: textResponse
});
}
// For image generation, proceed as normal
// Prepare the generation content
let generationContent;
if (drawingData) {
console.log('Including drawing data in generation request');
// If we have drawing data, include it in the request
generationContent = [
{
inlineData: {
data: drawingData,
mimeType: "image/png"
}
},
{ text: prompt }
];
} else {
console.log('Using text-only prompt for generation');
// Text-only prompt if no drawing
generationContent = [{ text: prompt }];
}
console.log(`Calling Gemini API for image generation (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 imageData = 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) {
imageData = part.inlineData.data;
console.log('Found image data in response');
break;
}
}
if (!imageData) {
console.error('No image data in response parts:', response.candidates[0].content.parts);
throw new Error('No image data found in response parts');
}
console.log('Successfully generated image');
return res.status(200).json({
success: true,
imageData: imageData
});
// If we reach here, we succeeded, so break out of the retry loop
break;
} 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('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/generate:', 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 generation.',
details: error.stack,
retries: retryCount
});
}
}
|