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
    });
  }
}