const fs = require('fs'); const content = fs.readFileSync('temp/rich-outline-fail_57_1779015509994.txt', 'utf-8'); const marker = '========== AI εŽŸε§‹ε“εΊ” '; const markerIdx = content.indexOf(marker); const headerEndIdx = content.indexOf(' ==========', markerIdx); const aiStart = content.indexOf('\n', headerEndIdx) + 1; const aiEnd = content.indexOf('\n\n', aiStart); const aiResponse = content.substring(aiStart, aiEnd); let openBraces = 0, closeBraces = 0, openBrackets = 0, closeBrackets = 0; let inString = false, escapeNext = false; for (let i = 0; i < aiResponse.length; i++) { const ch = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (ch === '\\') { escapeNext = true; continue; } if (ch === '"') { inString = !inString; continue; } if (inString) continue; if (ch === '{') openBraces++; else if (ch === '}') closeBraces++; else if (ch === '[') openBrackets++; else if (ch === ']') closeBrackets++; } console.log('Opening braces:', openBraces, 'Closing braces:', closeBraces, 'Diff:', openBraces - closeBraces); console.log('Opening brackets:', openBrackets, 'Closing brackets:', closeBrackets, 'Diff:', openBrackets - closeBrackets); console.log('Response ends with:', JSON.stringify(aiResponse.substring(aiResponse.length - 20))); // If braces are unbalanced, let's try to find where it becomes complete // by scanning through and finding where brace/bracket levels return to 0 let braceLevel = 0, bracketLevel = 0; let lastCompletePos = 0; inString = false; escapeNext = false; for (let i = 0; i < aiResponse.length; i++) { const ch = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (ch === '\\') { escapeNext = true; continue; } if (ch === '"') { inString = !inString; continue; } if (inString) continue; if (ch === '{') braceLevel++; else if (ch === '}') braceLevel--; else if (ch === '[') bracketLevel++; else if (ch === ']') bracketLevel--; if (braceLevel === 0 && bracketLevel === 0 && (ch === '}' || ch === ']')) { // Try parsing up to here const sub = aiResponse.substring(0, i + 1); try { const data = JSON.parse(sub); if (data.chapters && data.chapters.length > 0) { lastCompletePos = i + 1; console.log('Found complete valid JSON at position', i + 1, 'with', data.chapters.length, 'chapters'); } } catch(e) {} } } console.log('Last complete valid JSON position:', lastCompletePos); if (lastCompletePos > 0) { console.log('Partial JSON length:', lastCompletePos, 'of', aiResponse.length, '(' + Math.round(lastCompletePos/aiResponse.length*100) + '%)'); } // Try to find if we can truncate and still get valid partial JSON console.log('\n=== Trying truncation approach ==='); // The JSON is truncated (unbalanced). Let me try to find the last complete chapter const chaptersData = aiResponse.match(/"chapters"\s*:\s*\[([\s\S]*)/); if (chaptersData) { console.log('Chapters array starts at position:', aiResponse.indexOf('"chapters"')); // Try to find where the chapters array closes let bracketCount = 0; let chaptersEnd = -1; inString = false; escapeNext = false; const chaptersStartIdx = aiResponse.indexOf('"chapters"'); for (let i = chaptersStartIdx; i < aiResponse.length; i++) { const ch = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (ch === '\\') { escapeNext = true; continue; } if (ch === '"') { inString = !inString; continue; } if (inString) continue; if (ch === '[') bracketCount++; else if (ch === ']') { bracketCount--; if (bracketCount === 0) { chaptersEnd = i; break; } } } console.log('Chapters array ends at position:', chaptersEnd); if (chaptersEnd > 0) { const chaptersJson = '{"chapters":' + aiResponse.substring(chaptersStartIdx + 13, chaptersEnd + 1); console.log('Trying to parse chapters alone...'); try { const data = JSON.parse(chaptersJson); console.log('SUCCESS! Chapters count:', data.chapters.length); } catch(e) { console.log('FAILED:', e.message.substring(0, 100)); } } }