From 85b43e6608a7b4b53a96085069a214e0d413bce9 Mon Sep 17 00:00:00 2001 From: Sidney Date: Fri, 17 Jul 2026 22:31:31 -0300 Subject: [PATCH] fix: corrige geracao de imagens do Gemini usando compatibilidade OpenAI no Google AI Studio (generativelanguage) e adicionando novos modelos no frontend --- manager/components/Exams.tsx | 8 +++-- manager/server.selfhosted.js | 67 +++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/manager/components/Exams.tsx b/manager/components/Exams.tsx index daf5d04..4d32e66 100644 --- a/manager/components/Exams.tsx +++ b/manager/components/Exams.tsx @@ -40,7 +40,7 @@ const Exams: React.FC = ({ data, updateData }) => { openrouterModel: data.aiConfig?.openrouterModel || 'google/gemini-2.5-flash', activeImageProvider: data.aiConfig?.activeImageProvider || 'gemini', openaiImageModel: data.aiConfig?.openaiImageModel || 'dall-e-3', - geminiImageModel: data.aiConfig?.geminiImageModel || 'imagen-3.0-generate-002' + geminiImageModel: data.aiConfig?.geminiImageModel || 'gemini-2.5-flash-image' }); const [dynamicModels, setDynamicModels] = useState<{ @@ -93,7 +93,7 @@ const Exams: React.FC = ({ data, updateData }) => { openrouterModel: data.aiConfig?.openrouterModel || 'google/gemini-2.5-flash', activeImageProvider: data.aiConfig?.activeImageProvider || 'gemini', openaiImageModel: data.aiConfig?.openaiImageModel || 'dall-e-3', - geminiImageModel: data.aiConfig?.geminiImageModel || 'imagen-3.0-generate-002' + geminiImageModel: data.aiConfig?.geminiImageModel || 'gemini-2.5-flash-image' }); setShowAIConfigModal(true); }; @@ -1467,7 +1467,9 @@ const Exams: React.FC = ({ data, updateData }) => { onChange={e => setAiForm({ ...aiForm, geminiImageModel: e.target.value })} className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:outline-none transition-all font-bold text-slate-700 text-sm" > - + + + )} diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 1ad3bf0..e883274 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -1661,43 +1661,62 @@ app.post('/api/ai/generate-image', async (req, res) => { imageUrl = resJson.data?.[0]?.url; } } else { - // Usar Google Gemini Imagen 3 - const geminiImageModel = aiConfig.geminiImageModel || 'imagen-3.0-generate-002'; - const imagenUrl = `https://generativelanguage.googleapis.com/v1beta/models/${geminiImageModel}:predict?key=${apiKey}`; + // Usar Google Gemini Imagen via OpenAI Compatibility Layer + const geminiImageModel = aiConfig.geminiImageModel || 'gemini-2.5-flash-image'; + const imagenUrl = `https://generativelanguage.googleapis.com/v1beta/openai/images/generations?key=${apiKey}`; const payload = { - instances: [ - { - prompt: `Ilustração educativa flat vector sobre: ${prompt}` - } - ], - parameters: { - sampleCount: 1, - aspectRatio: "1:1", - outputMimeType: "image/jpeg" - } + prompt: `Ilustração educativa flat vector sobre: ${prompt}`, + n: 1, + size: '1024x1024', + model: geminiImageModel }; - const response = await fetch(imagenUrl, { + let response = await fetch(imagenUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); + // Se der 404, tentar com o modelo gemini-2.5-flash-image de fallback + if (!response.ok && response.status === 404 && geminiImageModel !== 'gemini-2.5-flash-image') { + console.warn(`[Gemini Imagen] Modelo ${geminiImageModel} retornou 404, tentando gemini-2.5-flash-image de fallback...`); + payload.model = 'gemini-2.5-flash-image'; + response = await fetch(imagenUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + } + + // Se der 404 novamente, tentar com gemini-3.1-flash-image + if (!response.ok && response.status === 404 && payload.model !== 'gemini-3.1-flash-image') { + console.warn(`[Gemini Imagen] Tentando gemini-3.1-flash-image de fallback...`); + payload.model = 'gemini-3.1-flash-image'; + response = await fetch(imagenUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + } + + // Fallback final para imagen-3.0-generate-002 se as flash image falharem + if (!response.ok && response.status === 404 && payload.model !== 'imagen-3.0-generate-002') { + console.warn(`[Gemini Imagen] Tentando imagen-3.0-generate-002 de fallback final...`); + payload.model = 'imagen-3.0-generate-002'; + response = await fetch(imagenUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + } + if (!response.ok) { const errText = await response.text(); - throw new Error(`Gemini Imagen retornou erro: ${errText}`); + throw new Error(`Gemini Imagen retornou erro: ${response.status} - ${errText}`); } const resJson = await response.json(); - const base64ImageBytes = resJson.predictions?.[0]?.bytesBase64Encoded; - if (!base64ImageBytes) { - throw new Error('Retorno vazio da API Imagen do Gemini'); - } - - const imageBuffer = Buffer.from(base64ImageBytes, 'base64'); - const { uploadExamImage } = await import('./services/storage.js'); - const savedUrl = await uploadExamImage(imageBuffer, 'image/jpeg'); - return res.json({ url: savedUrl }); + imageUrl = resJson.data?.[0]?.url; } if (!imageUrl) {