CoolFace
Apppublic

Leon4gr45/builder

sourceHugging Facemitupdated 2d agoView on Hugging Face
0likes
content-normalizer.ts119 linesDownload Raw Back to markdown-renderer
1/**2 * Content normalizer for LLM output before markdown rendering3 * Fixes formatting issues from models that output excessive whitespace4 */5 6/**7 * Normalizes content to fix common LLM output formatting issues8 * - Removes excessive leading whitespace that causes false code blocks9 * - Preserves intentional markdown formatting (fenced code blocks, lists, etc.)10 * - Normalizes excessive blank lines11 */12export function normalizeContent(content: string): string {13  if (!content || typeof content !== 'string') return '';14 15  const lines = content.split('\n');16  const normalized: string[] = [];17  let inFencedCodeBlock = false;18  let consecutiveBlankLines = 0;19 20  // Detect if a line is a fenced code block delimiter21  const isFencedCodeDelimiter = (line: string): boolean => {22    const trimmed = line.trim();23    return /^```/.test(trimmed);24  };25 26  // Detect if a line is an intentional list item27  const isListItem = (line: string): boolean => {28    const trimmed = line.trim();29    return /^[-*+]\s/.test(trimmed) || /^\d+\.\s/.test(trimmed);30  };31 32  // Detect if a line is an intentional blockquote33  const isBlockquote = (line: string): boolean => {34    return /^\s*>/.test(line);35  };36 37  // Detect if line is likely code (heuristic)38  const looksLikeCode = (line: string): boolean => {39    const trimmed = line.trim();40    // Empty lines or very short lines are not code41    if (!trimmed || trimmed.length < 3) return false;42 43    // Check for code-like patterns44    const codePatterns = [45      /^(const|let|var|function|class|import|export|return|if|for|while)\s/,46      /^[a-zA-Z_$][a-zA-Z0-9_$]*\s*[=:({]/,47      /[{};()[\]]/,48      /^\/\//,49      /^#/,50    ];51 52    return codePatterns.some(pattern => pattern.test(trimmed));53  };54 55  for (let i = 0; i < lines.length; i++) {56    const line = lines[i];57 58    // Track fenced code block state59    if (isFencedCodeDelimiter(line)) {60      inFencedCodeBlock = !inFencedCodeBlock;61      normalized.push(line);62      consecutiveBlankLines = 0;63      continue;64    }65 66    // Inside fenced code blocks, preserve everything as-is67    if (inFencedCodeBlock) {68      normalized.push(line);69      consecutiveBlankLines = 0;70      continue;71    }72 73    // Handle blank lines74    if (!line.trim()) {75      consecutiveBlankLines++;76      // Allow max 2 consecutive blank lines77      if (consecutiveBlankLines <= 2) {78        normalized.push('');79      }80      continue;81    }82 83    consecutiveBlankLines = 0;84 85    // Preserve list items with their indentation86    if (isListItem(line)) {87      normalized.push(line);88      continue;89    }90 91    // Preserve blockquotes92    if (isBlockquote(line)) {93      normalized.push(line);94      continue;95    }96 97    // Check for excessive leading whitespace98    const leadingSpaces = line.match(/^(\s*)/)?.[1].length || 0;99 100    // If line has 4+ leading spaces and doesn't look like code, trim it101    if (leadingSpaces >= 4 && !looksLikeCode(line)) {102      // This is likely accidental indentation from the model103      normalized.push(line.trim());104      continue;105    }106 107    // If line has 2-3 leading spaces, reduce to 0 (likely unintentional)108    if (leadingSpaces >= 2 && leadingSpaces < 4) {109      normalized.push(line.trim());110      continue;111    }112 113    // Otherwise preserve the line as-is114    normalized.push(line);115  }116 117  return normalized.join('\n').trim();118}119