UNPKG

ai-json-fixer

Version:

A simple JSON parser designed to handle malformed JSON from Large Language Models

190 lines 6.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LLMJSONParser = void 0; const markdown_extraction_1 = require("../fixes/markdown-extraction"); const trailing_content_removal_1 = require("../fixes/trailing-content-removal"); const quote_fixing_1 = require("../fixes/quote-fixing"); const missing_comma_detection_1 = require("../fixes/missing-comma-detection"); /** * Main parser class for handling malformed JSON from Large Language Models */ class LLMJSONParser { constructor() { this.defaultOptions = { mode: 'standard', stripMarkdown: true, trimTrailing: true, fixQuotes: true, addMissingCommas: true, completeStructure: false, coerceTypes: false, escapeCharacters: false, trackFixes: false, throwOnError: false, maxFixAttempts: 3, }; } /** * Parse JSON input with automatic fixing of common LLM output issues */ parse(input, options) { const opts = { ...this.defaultOptions, ...options }; const result = this.tryParse(input, options); if (result.data === null && opts.throwOnError) { throw new Error(`Failed to parse JSON: ${result.warnings?.join(', ') || 'Unknown error'}`); } return result.data; } /** * Try to parse JSON input, returning detailed results including fixes and warnings */ tryParse(input, options) { const opts = { ...this.defaultOptions, ...options }; const fixes = []; const warnings = []; // Handle empty input if (!input || input.trim() === '') { return { data: null, fixes: fixes, warnings: ['Input is empty'], }; } let processedInput = input; let attemptCount = 0; while (attemptCount < opts.maxFixAttempts) { attemptCount++; try { // Try parsing as-is first const parsed = JSON.parse(processedInput); return { data: parsed, fixes: opts.trackFixes ? fixes : undefined, confidence: this.calculateConfidence(fixes.length), warnings: warnings.length > 0 ? warnings : undefined, }; } catch (parseError) { // Apply fixes based on mode and options const fixedInput = this.applyFixes(processedInput, opts, fixes); if (fixedInput === processedInput) { // No fixes were applied, parsing failed warnings.push(`Parse attempt ${attemptCount} failed: ${parseError.message}`); break; } processedInput = fixedInput; } } // Final attempt after all fixes try { const parsed = JSON.parse(processedInput); return { data: parsed, fixes: opts.trackFixes ? fixes : undefined, confidence: this.calculateConfidence(fixes.length), warnings: warnings.length > 0 ? warnings : undefined, }; } catch (finalError) { warnings.push(`Final parse failed: ${finalError.message}`); return { data: null, fixes: opts.trackFixes ? fixes : undefined, confidence: 0, warnings, }; } } /** * Apply fixes to the input based on options and mode */ applyFixes(input, options, fixes) { let processed = input; // 1. Extract from markdown blocks if (options.stripMarkdown) { const extracted = (0, markdown_extraction_1.extractFromMarkdown)(processed); if (extracted !== processed) { processed = extracted; if (options.trackFixes) { fixes.push({ type: 'markdown_stripped', line: 0, column: 0, description: 'Extracted JSON from markdown code block', }); } } } // 2. Remove trailing content if (options.trimTrailing) { const trimmed = (0, trailing_content_removal_1.removeTrailingContent)(processed); if (trimmed !== processed) { processed = trimmed; if (options.trackFixes) { fixes.push({ type: 'trailing_removed', line: 0, column: 0, description: 'Removed trailing content after JSON', }); } } } // 3. Fix unescaped quotes if (options.fixQuotes) { const quotesFixed = (0, quote_fixing_1.fixUnescapedQuotes)(processed); if (quotesFixed !== processed) { processed = quotesFixed; if (options.trackFixes) { fixes.push({ type: 'unescaped_quote', line: 0, column: 0, description: 'Fixed unescaped quotes in strings', }); } } } // 4. Add missing commas if (options.addMissingCommas) { const commasAdded = (0, missing_comma_detection_1.addMissingCommas)(processed); if (commasAdded !== processed) { processed = commasAdded; if (options.trackFixes) { fixes.push({ type: 'missing_comma', line: 0, column: 0, description: 'Added missing commas between elements', }); } } } // Mode-specific fixes if (options.mode === 'aggressive') { // In aggressive mode, apply additional fixes if needed // For now, this is the same as standard mode } else if (options.mode === 'strict') { // In strict mode, only apply fixes if they're explicitly enabled // Current implementation already handles this via options } return processed; } /** * Calculate confidence score based on number of fixes applied */ calculateConfidence(fixCount) { if (fixCount === 0) return 1.0; if (fixCount === 1) return 0.9; if (fixCount === 2) return 0.8; if (fixCount === 3) return 0.7; return Math.max(0.5, 1.0 - fixCount * 0.1); } } exports.LLMJSONParser = LLMJSONParser; //# sourceMappingURL=llm-json-parser.js.map