| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- 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);
- console.log('String length (UTF-16 code units):', aiResponse.length);
- // The issue: JSON.parse uses UTF-16 code unit positions, but the JSON is actually UTF-8 encoded
- // when transmitted over the network. The LLM returns UTF-8 bytes, but in Node.js when we get
- // the response as a string, it's decoded to UTF-16.
- // Wait - actually JSON.parse should work correctly with UTF-16 strings in JavaScript.
- // Let me check if there's actually a malformed character somewhere.
- // Try using a simple character-by-character JSON parser approach to find the issue
- const pos = 1624;
- console.log('\nAnalyzing around position', pos);
- // Get the actual bytes via TextEncoder
- const encoder = new TextEncoder();
- const decoder = new TextDecoder('utf-8');
- // Let's check the raw bytes of the string
- const uint8Array = encoder.encode(aiResponse);
- console.log('Encoded bytes length:', uint8Array.length);
- // Check bytes around position 1624 (in UTF-16 string)
- console.log('\nBytes around UTF-16 position', pos, ':');
- for (let i = Math.max(0, pos - 5); i < Math.min(uint8Array.length, pos + 10); i++) {
- console.log(' byte', i, ':', uint8Array[i], '(char:', String.fromCharCode(uint8Array[i]), ')');
- }
- // Now let's try to find where the actual byte-level position differs from UTF-16 position
- // by counting UTF-8 multi-byte characters before position 1624
- let utf8Offset = 0;
- let utf16Offset = 0;
- let lastAsciiBefore1624 = -1;
- let lastAsciiUtf8BytePos = -1;
- for (let i = 0; i < aiResponse.length && utf16Offset < 1624; i++) {
- const code = aiResponse.charCodeAt(i);
- if (code <= 0x7F) {
- // ASCII - 1 byte in UTF-8, 1 code unit in UTF-16
- if (utf16Offset === 1623) {
- lastAsciiBefore1624 = code;
- lastAsciiUtf8BytePos = utf8Offset;
- }
- utf8Offset += 1;
- utf16Offset += 1;
- } else if (code <= 0x7FF) {
- // 2-byte UTF-8
- utf8Offset += 2;
- utf16Offset += 1;
- } else if (code <= 0xFFFF) {
- // 3-byte UTF-8 (most CJK characters)
- utf8Offset += 3;
- utf16Offset += 1;
- } else {
- // 4-byte UTF-8 (emoji, etc)
- utf8Offset += 4;
- utf16Offset += 2;
- }
- }
- console.log('\nAt UTF-16 position 1624:');
- console.log(' Corresponding UTF-8 byte position would be around:', utf8Offset);
- console.log(' Last ASCII char (at UTF-16 1623):', lastAsciiBefore1624, '(code:', lastAsciiBefore1624, ')');
- // Now let's check what JSON.parse says the error position actually means
- // In JSON parse errors, "position" is the character offset in the string
- // Let's see if position 1624 in the string is actually where the issue is
- console.log('\n=== Direct parse attempt ===');
- try {
- JSON.parse(aiResponse);
- } catch(e) {
- console.log('Error:', e.message);
- const match = e.message.match(/position (\d+)/);
- if (match) {
- const errorPos = parseInt(match[1]);
- console.log('Error position:', errorPos);
- console.log('Character at error position:', JSON.stringify(aiResponse[errorPos]));
- console.log('Context:', JSON.stringify(aiResponse.substring(Math.max(0, errorPos-50), errorPos+50)));
- // Check if char at errorPos is the start of a multi-byte sequence
- const code = aiResponse.charCodeAt(errorPos);
- console.log('Code point at errorPos:', code, 'hex:', code.toString(16));
- }
- }
|