Fix JSON parse for Sonnet 4.6: escape literal newlines in strings
Sonnet 4.6 outputs literal newline characters inside JSON string values (e.g. in HTML body field) instead of \n escape sequences. Added sanitizeJsonString that walks the string character by character and escapes unescaped control chars inside quoted values.
This commit is contained in:
parent
4b62d073f7
commit
5e3c95d163
1 changed files with 32 additions and 10 deletions
|
|
@ -242,24 +242,46 @@ router.post('/ai-generate', upload.single('file'), async function(req, res) {
|
||||||
var lastBrace = raw.lastIndexOf('}');
|
var lastBrace = raw.lastIndexOf('}');
|
||||||
if (lastBrace !== -1 && lastBrace < raw.length - 1) raw = raw.substring(0, lastBrace + 1);
|
if (lastBrace !== -1 && lastBrace < raw.length - 1) raw = raw.substring(0, lastBrace + 1);
|
||||||
|
|
||||||
|
// Fix unescaped control characters inside JSON string values.
|
||||||
|
// Models sometimes output literal newlines/tabs inside strings instead of \n \t.
|
||||||
|
function sanitizeJsonString(s) {
|
||||||
|
var inStr = false, escaped = false, out = '';
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
var c = s[i];
|
||||||
|
if (escaped) { out += c; escaped = false; continue; }
|
||||||
|
if (c === '\\' && inStr) { out += c; escaped = true; continue; }
|
||||||
|
if (c === '"') { inStr = !inStr; out += c; continue; }
|
||||||
|
if (inStr && c === '\n') { out += '\\n'; continue; }
|
||||||
|
if (inStr && c === '\r') { continue; }
|
||||||
|
if (inStr && c === '\t') { out += '\\t'; continue; }
|
||||||
|
out += c;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
var parsed;
|
var parsed;
|
||||||
try {
|
try {
|
||||||
parsed = JSON.parse(raw);
|
parsed = JSON.parse(raw);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Try extracting from first { to last }
|
console.error('[LearningAI] Direct parse failed:', e.message, '| Pos:', e.message.match(/position (\d+)/)?.[1], '| Char context:', raw.substring(parseInt(e.message.match(/position (\d+)/)?.[1] || 0) - 40, parseInt(e.message.match(/position (\d+)/)?.[1] || 0) + 40));
|
||||||
|
// Attempt 2: sanitize control chars and retry
|
||||||
|
try { parsed = JSON.parse(sanitizeJsonString(raw)); }
|
||||||
|
catch (e2) {
|
||||||
|
// Attempt 3: extract first { to last }
|
||||||
var firstBrace = raw.indexOf('{');
|
var firstBrace = raw.indexOf('{');
|
||||||
var extractedJson = firstBrace >= 0 ? raw.substring(firstBrace, lastBrace + 1) : null;
|
var extractedJson = firstBrace >= 0 ? raw.substring(firstBrace, lastBrace + 1) : null;
|
||||||
if (extractedJson) {
|
if (extractedJson) {
|
||||||
try { parsed = JSON.parse(extractedJson); }
|
try { parsed = JSON.parse(sanitizeJsonString(extractedJson)); }
|
||||||
catch (e2) {
|
catch (e3) {
|
||||||
console.error('[LearningAI] JSON parse failed. Raw (first 500):', raw.substring(0, 500));
|
console.error('[LearningAI] All parse attempts failed:', e3.message);
|
||||||
return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' });
|
return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('[LearningAI] No JSON found. Raw (first 500):', raw.substring(0, 500));
|
console.error('[LearningAI] No JSON braces found');
|
||||||
return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' });
|
return res.status(500).json({ error: 'AI returned invalid JSON. Try again.' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue