diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 93e94bc..3ca75f4 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -1565,8 +1565,25 @@ Apenas retorne o JSON estruturado contendo a lista de questões no seguinte form } } + // Se der erro 400 por conta de Structured Outputs incompatíveis com o modelo selecionado, re-tentamos sem eles + let errText = ''; + if (!response.ok && response.status === 400) { + errText = await response.text(); + if (errText.includes('responseMimeType') || errText.includes('responseSchema')) { + console.warn(`[Gemini] Modelo ${modelName} não aceita Structured Outputs. Re-tentando chamada sem responseMimeType/Schema...`); + const simplePayload = { + contents: payload.contents + }; + response = await fetch(geminiUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(simplePayload) + }); + } + } + if (!response.ok) { - const errorText = await response.text(); + const errorText = errText || await response.text(); throw new Error(`Gemini API retornou erro: ${response.status} - ${errorText}`); } @@ -1576,7 +1593,19 @@ Apenas retorne o JSON estruturado contendo a lista de questões no seguinte form throw new Error('Retorno vazio da API do Gemini'); } - const questions = JSON.parse(textResponse); + // Limpar blocos de código markdown se o modelo retornou em formato livre + let cleanText = textResponse.trim(); + if (cleanText.startsWith('```json')) { + cleanText = cleanText.substring(7); + } else if (cleanText.startsWith('```')) { + cleanText = cleanText.substring(3); + } + if (cleanText.endsWith('```')) { + cleanText = cleanText.substring(0, cleanText.length - 3); + } + cleanText = cleanText.trim(); + + const questions = JSON.parse(cleanText); return res.json({ questions }); } } catch (error) {