diff --git a/src/routes/learningAI.js b/src/routes/learningAI.js index feb7b5f..86f6526 100644 --- a/src/routes/learningAI.js +++ b/src/routes/learningAI.js @@ -202,11 +202,19 @@ router.post('/ai-generate', upload.single('file'), async function(req, res) { var prompt = buildGeneratePrompt({ topic, docText, contentType, questionCount, refinement, wordCount, slideCount }); var result = await callAI( - [{ role: 'user', content: prompt }], + [ + { role: 'system', content: 'You are a medical education content generator. Return ONLY the requested JSON or Marp markdown — no preamble, no commentary, no code fences, no thinking. Start your response with { or --- as appropriate.' }, + { role: 'user', content: prompt } + ], { model: model, temperature: 0.4, maxTokens: 6000 } ); var raw = result.content.trim(); + // Strip any leading text before the first { or --- (models sometimes add preamble) + if (contentType !== 'presentation' || parseInt(questionCount) > 0) { + var jsonStart = raw.indexOf('{'); + if (jsonStart > 0) raw = raw.substring(jsonStart); + } // ── Presentation ── if (contentType === 'presentation') { @@ -228,18 +236,27 @@ router.post('/ai-generate', upload.single('file'), async function(req, res) { return res.json({ success: true, contentType: 'presentation', marpMarkdown: marpMd, questions: [], model: result.model, docLength: docText.length }); } - // Strip code fences if model added them anyway + // Strip code fences and any trailing text after JSON raw = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```\s*$/, ''); + // Trim trailing non-JSON text (e.g. "Here is the JSON..." after closing brace) + var lastBrace = raw.lastIndexOf('}'); + if (lastBrace !== -1 && lastBrace < raw.length - 1) raw = raw.substring(0, lastBrace + 1); var parsed; try { parsed = JSON.parse(raw); } catch (e) { - var match = raw.match(/\{[\s\S]*\}/); - if (match) { - try { parsed = JSON.parse(match[0]); } - catch (e2) { return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' }); } + // Try extracting from first { to last } + var firstBrace = raw.indexOf('{'); + var extractedJson = firstBrace >= 0 ? raw.substring(firstBrace, lastBrace + 1) : null; + if (extractedJson) { + try { parsed = JSON.parse(extractedJson); } + catch (e2) { + console.error('[LearningAI] JSON parse failed. Raw (first 500):', raw.substring(0, 500)); + return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' }); + } } else { + console.error('[LearningAI] No JSON found. Raw (first 500):', raw.substring(0, 500)); return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' }); } }