Severian commited on
Commit
7b642ea
·
verified ·
1 Parent(s): d8d16ef

Update services/difyService.ts

Browse files
Files changed (1) hide show
  1. services/difyService.ts +841 -261
services/difyService.ts CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import { API_URL, API_TOKEN, API_USER } from '../constants';
3
  import { ApiInput, ApiResponseOutput, ProcessedResult, QaSectionResult, DetailedQaReport } from '../types';
4
 
@@ -67,12 +66,19 @@ const parseSection = (sectionText: string): QaSectionResult => {
67
  }
68
  console.log('Grade match result:', gradeMatch, 'Final grade:', grade);
69
 
70
- // ROBUST PASS EXTRACTION - handles multiple formats
71
  let pass = false;
72
  let passMatch = null;
73
 
74
- // Try various pass patterns in order of specificity
75
  const passPatterns = [
 
 
 
 
 
 
 
76
  /-\s*\*\*Pass:\*\*\s*(.*)/i, // - **Pass:** true
77
  /•\s*\*\*Pass:\*\*\s*(.*)/i, // • **Pass:** true
78
  /\*\s*\*\*Pass:\*\*\s*(.*)/i, // * **Pass:** true
@@ -89,11 +95,11 @@ const parseSection = (sectionText: string): QaSectionResult => {
89
  passMatch = sectionText.match(pattern);
90
  if (passMatch) {
91
  const passValue = passMatch[1].toLowerCase().trim();
92
- pass = passValue.includes('true') ||
 
93
  passValue.includes('✅') ||
94
  passValue === 'yes' ||
95
- passValue === 'passed' ||
96
- passValue === 'pass';
97
  break;
98
  }
99
  }
@@ -139,6 +145,22 @@ const parseSection = (sectionText: string): QaSectionResult => {
139
  }
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  // ROBUST ANALYSIS/CORRECTED CONTENT EXTRACTION - handles multiple formats
143
  let corrected = 'Content analysis not available.';
144
  let contentMatch = null;
@@ -179,6 +201,7 @@ const parseSection = (sectionText: string): QaSectionResult => {
179
  corrected = corrected.replace(/^#\s*/, '').replace(/###\s*\*\*[^*]+\*\*/, '').trim();
180
  console.log('Content match result:', contentMatch, 'Final corrected:', corrected.substring(0, 50));
181
 
 
182
  return { grade, pass, errors, corrected };
183
  };
184
 
@@ -400,132 +423,266 @@ const parseEnhancedSection = (sectionBlock: string): QaSectionResult => {
400
  * Enhanced overall section parsing that captures ALL QA Guard content
401
  */
402
  const parseEnhancedOverallSection = (sectionBlock: string): { grade: string; pass: boolean; primaryIssue: string; detailedAssessment?: string; keyStrengths?: string[]; recommendations?: string[]; explanations?: string; rawContent?: string } => {
403
- // ROBUST OVERALL GRADE EXTRACTION - handles multiple formats
 
 
404
  let grade = 'N/A';
405
- let gradeMatch = null;
 
 
 
 
 
406
 
 
 
407
  const overallGradePatterns = [
408
- /-\s*\*\*Final Grade:\*\*\s*(.*)/, // - **Final Grade:** 100/100
409
- /•\s*\*\*Final Grade:\*\*\s*(.*)/, // **Final Grade:** 100/100
410
- /\*\s*\*\*Final Grade:\*\*\s*(.*)/, // * **Final Grade:** 100/100
411
- /-\s*\*\*Total Grade:\*\*\s*(.*)/, // - **Total Grade:** 100/100
412
- /•\s*\*\*Total Grade:\*\*\s*(.*)/, // **Total Grade:** 100/100
413
- /\*\s*\*\*Total Grade:\*\*\s*(.*)/, // * **Total Grade:** 100/100
414
- /(?:•|-|\*)\s*\*\*(?:Final|Total|Overall)?\s*Grade\*\*:?:\s*(.*)/i, // Generic grade
415
- /(?:•|-|\*)\s*(?:Final|Total|Overall)?\s*Grade:?:\s*(.*)/i, // No bold
416
- /(?:Final|Total|Overall)\s*Grade:?:\s*(\d+\/\d+|\d+)/im, // Anywhere in text
417
- /(\d+\/\d+)\s*(?:final|total|overall)?/im // Number first
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  ];
419
 
 
420
  for (const pattern of overallGradePatterns) {
421
- gradeMatch = sectionBlock.match(pattern);
422
- if (gradeMatch) {
423
- grade = gradeMatch[1].trim();
 
424
  break;
425
  }
426
  }
427
- console.log('Overall grade match:', gradeMatch, 'Final grade:', grade);
428
-
429
- // ROBUST OVERALL PASS EXTRACTION - handles multiple formats
430
- let pass = false;
431
- let passMatch = null;
432
 
 
 
433
  const overallPassPatterns = [
434
- // Exact format variations found in logs
435
- /-\s*\*\*Overall Pass:\*\*\s*(.*)/i, // - **Overall Pass:** true
436
- /•\s*\*\*Overall Pass:\*\*\s*(.*)/i, // **Overall Pass:** true
437
- /\*\s*\*\*Overall Pass:\*\*\s*(.*)/i, // * **Overall Pass:** true
438
- /•\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // **All Sections Pass:** true
439
- /-\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // - **All Sections Pass:** true
440
- /\*\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // * **All Sections Pass:** true
441
- /•\s*\*\*Final Pass:\*\*\s*(.*)/i, // **Final Pass:** true
442
- /-\s*\*\*Final Pass:\*\*\s*(.*)/i, // - **Final Pass:** true
443
- /\*\s*\*\*Final Pass:\*\*\s*(.*)/i, // * **Final Pass:** true
444
-
445
- // Generic patterns with flexible formatting
446
- /(?:•|-|\*)\s*\*\*(?:Overall\s+|All\s+Sections\s+|Final\s+)?Pass\*\*:?:\s*(.*)/i,
447
- /(?:•|-|\*)\s*(?:Overall\s+|All\s+Sections\s+|Final\s+)?Pass:?:\s*(.*)/i,
448
-
449
- // Anywhere in text patterns
450
- /Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
451
- /Overall\s*Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
452
- /All\s*Sections\s*Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
453
- /(true|false|✅|❌|TRUE|FALSE)\s*(?:overall|pass)/im,
454
-
455
- // Handle capitalized boolean values
456
- /Pass:?:\s*(True|False|TRUE|FALSE)/im,
457
- /Overall\s*Pass:?:\s*(True|False|TRUE|FALSE)/im
458
  ];
459
 
 
460
  for (const pattern of overallPassPatterns) {
461
- passMatch = sectionBlock.match(pattern);
462
- if (passMatch) {
463
- const passValue = passMatch[1].toLowerCase().trim();
464
- pass = passValue.includes('true') ||
465
- passValue.includes('✅') ||
466
- passValue === 'yes' ||
467
- passValue === 'passed' ||
468
- passValue === 'pass';
469
  break;
470
  }
471
  }
472
- console.log('Overall pass match:', passMatch, 'Final pass:', pass);
473
 
474
- // Look for various primary issue formats
475
- const explanationMatch = sectionBlock.match(/\*\*Overall Pass\*\*:\s*[^()]*\(([^)]+)\)/);
476
- const statusMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Pipeline\s+)?Status\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
477
- const primaryIssueMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Primary\s+)?Issue\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/) ||
478
- sectionBlock.match(/(?:•|-|\*)\s*(?:Primary\s+)?Issue:?:\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
479
- const errorsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*Total\s+Errors?\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
480
- const totalSectionsMatch = sectionBlock.match(/Total\s*Sections\s*Passing:?:\s*([^\n]+)/i);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
- let primaryIssue = 'All sections passed successfully.';
483
- if (explanationMatch) {
484
- primaryIssue = explanationMatch[1].trim();
485
- } else if (statusMatch) {
486
- primaryIssue = statusMatch[1].trim();
487
- } else if (primaryIssueMatch) {
488
- primaryIssue = primaryIssueMatch[1].trim();
489
- } else if (errorsMatch) {
490
- const errorText = errorsMatch[1].trim();
491
- if (errorText !== '[]' && errorText !== '') {
492
- primaryIssue = `Errors found: ${errorText}`;
493
  }
494
  }
495
- if (totalSectionsMatch) {
496
- primaryIssue = `${primaryIssue} | Total Sections Passing: ${totalSectionsMatch[1].trim()}`;
497
- }
498
 
499
- console.log('Primary issue extraction - explanation:', explanationMatch, 'status:', statusMatch, 'issue:', primaryIssueMatch, 'Final issue:', primaryIssue);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
 
501
- // Extract detailed assessment content
502
- const detailedAssessmentMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Detailed\s+)?Assessment\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
503
- const explanationsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Explanation|Reasoning)\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
 
 
 
 
 
 
 
 
 
 
504
 
505
  // Extract key strengths
506
- const keyStrengthsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*Key\s+Strengths\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
507
- const strengthsList = keyStrengthsMatch ?
508
- keyStrengthsMatch[1].split('\n')
509
- .map(line => line.replace(/^[-•*]\s*/, '').trim())
510
- .filter(line => line.length > 0) : undefined;
 
 
 
 
 
 
 
 
 
 
 
 
 
511
 
512
  // Extract recommendations
513
- const recommendationsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Recommendations?|Suggestions?)\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
514
- const recommendationsList = recommendationsMatch ?
515
- recommendationsMatch[1].split('\n')
516
- .map(line => line.replace(/^[-•*]\s*/, '').trim())
517
- .filter(line => line.length > 0) : undefined;
 
 
 
 
 
 
 
 
 
 
 
 
 
518
 
519
- console.log('Setting overall data - grade:', grade, 'pass:', pass, 'primaryIssue:', primaryIssue);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
 
521
- return {
522
- grade,
523
- pass,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  primaryIssue,
525
- detailedAssessment: detailedAssessmentMatch ? detailedAssessmentMatch[1].trim() : undefined,
526
- explanations: explanationsMatch ? explanationsMatch[1].trim() : undefined,
527
- keyStrengths: strengthsList,
528
- recommendations: recommendationsList,
529
  rawContent: sectionBlock
530
  };
531
  };
@@ -571,184 +728,607 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
571
  return { detailedQaReport: defaultReport, overallPass: false, overallGrade: 'N/A' };
572
  }
573
 
574
- // Check if it's the new markdown format (starts with ##) or contains **TITLE** style sections
575
- if (cleanedQaText.startsWith('##') || cleanedQaText.includes('**TITLE**') || cleanedQaText.includes('### **') || cleanedQaText.includes('### TITLE')) {
576
- console.log('Using enhanced markdown parser for QA report');
577
- console.log('QA text starts with:', cleanedQaText.substring(0, 200));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
 
579
- // Handle different section header formats
580
- let sections;
581
- if (cleanedQaText.includes('### **')) {
582
- // Split on ### ** and keep the ** in the section content
583
- sections = cleanedQaText.split(/(?=### \*\*)/g).slice(1);
584
- console.log('Splitting on ### ** (keeping section headers)');
585
- } else if (cleanedQaText.includes('**TITLE**')) {
586
- // Split on bold section headers like **TITLE**, **META**, etc.
587
- sections = cleanedQaText.split(/(?=\*\*(?:TITLE|META|H1|COPY|OVERALL|ASSESSMENT|CORRECTED COPY)[^*]*\*\*)/g).slice(1);
588
- console.log('Splitting on **SECTION** headers');
589
- } else if (cleanedQaText.includes('### ')) {
590
- // Split generic ### headers
591
- sections = cleanedQaText.split(/(?=###\s+)/g).slice(1);
592
- console.log('Splitting on generic ### headers');
 
 
 
 
 
 
 
 
 
 
593
  } else {
594
- sections = cleanedQaText.split('## ').slice(1);
595
- console.log('Splitting on ## headers');
 
 
 
 
 
 
596
  }
597
- console.log('Found sections:', sections.length);
598
- sections.forEach((section, index) => {
599
- console.log(`Section ${index}:`, section.substring(0, 100));
600
- });
 
601
 
602
- const parsedData: Partial<DetailedQaReport> = {};
603
- let correctedCopyFromSeparateSection = '';
604
- const additionalSections: { [sectionName: string]: { content: string; type: 'assessment' | 'strengths' | 'recommendations' | 'explanations' | 'other'; } } = {};
 
 
605
 
606
- // Special handling for single-section format like "## GRADE REPORT"
607
- if (sections.length === 1 && (sections[0].includes('GRADE REPORT') || sections[0].includes('QUALITY ASSURANCE'))) {
608
- console.log('Detected single-section format, parsing embedded sections');
609
- return parseSingleSectionFormat(sections[0], defaultReport);
 
 
 
 
 
610
  }
611
 
612
- sections.forEach(sectionBlock => {
613
- const lines = sectionBlock.trim().split('\n');
614
- let headerRaw = lines[0].trim();
615
- let header = headerRaw.toLowerCase();
616
-
617
- // Clean up header - remove markdown formatting and punctuation
618
- header = header.replace(/^#+\s*/, '').replace(/\*\*/g, '').replace(/[:\-–]+$/, '').trim();
619
- console.log('Processing header:', header);
620
-
621
- // Enhanced section parsing to capture ALL content
622
- if (header.includes('title')) {
623
- console.log('Parsing title section with enhanced content capture');
624
- parsedData.title = parseEnhancedSection(sectionBlock);
625
- } else if (header.includes('meta')) {
626
- console.log('Parsing meta section with enhanced content capture');
627
- parsedData.meta = parseEnhancedSection(sectionBlock);
628
- } else if (header.includes('h1')) {
629
- console.log('Parsing h1 section with enhanced content capture');
630
- parsedData.h1 = parseEnhancedSection(sectionBlock);
631
- } else if (header.includes('copy') && !header.includes('corrected')) {
632
- console.log('Parsing copy section with enhanced content capture');
633
- parsedData.copy = parseEnhancedSection(sectionBlock);
634
- } else if (header.includes('corrected') && header.includes('copy')) {
635
- console.log('Capturing separate CORRECTED COPY section');
636
- correctedCopyFromSeparateSection = lines.slice(1).join('\n').trim();
637
- } else if (header.includes('overall') || header.includes('assessment') || header.includes('pipeline')) {
638
- console.log('Parsing overall section with enhanced content capture');
639
- console.log('Overall section text:', sectionBlock.substring(0, 300));
640
-
641
- // Enhanced overall section parsing to capture ALL content
642
- const enhancedOverall = parseEnhancedOverallSection(sectionBlock);
643
- parsedData.overall = enhancedOverall;
644
- } else {
645
- // Capture any additional sections that don't match standard patterns
646
- console.log('Capturing additional section:', header);
647
- const sectionType = determineSectionType(sectionBlock);
648
- additionalSections[header] = {
649
- content: sectionBlock,
650
- type: sectionType
651
- };
652
- }
653
- });
654
-
655
- const finalReport: DetailedQaReport = {
656
- title: parsedData.title || { ...defaultSection, errors: ['Title section not found'] },
657
- meta: parsedData.meta || { ...defaultSection, errors: ['Meta section not found'] },
658
- h1: parsedData.h1 || { ...defaultSection, errors: ['H1 section not found'] },
659
- copy: parsedData.copy || { ...defaultSection, errors: ['Copy section not found'] },
660
- overall: parsedData.overall || { grade: 'N/A', pass: false, primaryIssue: 'Overall section not found' },
661
- additionalSections: Object.keys(additionalSections).length > 0 ? additionalSections : undefined,
662
- completeRawReport: qaText
663
- };
664
 
665
- // If we saw a separate CORRECTED COPY section, populate copy.corrected with it when useful
666
- if (correctedCopyFromSeparateSection) {
667
- const cleanedCorrected = correctedCopyFromSeparateSection.replace(/^###.*$/m, '').trim();
668
- if (!finalReport.copy.corrected || finalReport.copy.corrected.length < 20) {
669
- finalReport.copy.corrected = cleanedCorrected;
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  }
671
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
 
673
- // If no explicit overall section was found, check for inline overall results at end of text
674
- if (!parsedData.overall) {
675
- // Look for overall results anywhere in the full text (sometimes appears at the end)
676
- const inlineOverallGradeMatch = cleanedQaText.match(/##?\s*OVERALL\s*(?:PIPELINE\s*)?GRADE:?:\s*([^#\n]+)/i);
677
- const inlineOverallPassMatch = cleanedQaText.match(/##?\s*OVERALL\s*(?:PIPELINE\s*)?PASS:?:\s*([^#\n(]+)/i);
678
-
679
- if (inlineOverallGradeMatch || inlineOverallPassMatch) {
 
 
680
  let grade = 'N/A';
681
- let pass = false;
682
-
683
- if (inlineOverallGradeMatch) {
684
- grade = inlineOverallGradeMatch[1].trim();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  }
686
-
687
- if (inlineOverallPassMatch) {
688
- const passValue = inlineOverallPassMatch[1].toLowerCase().trim();
689
- pass = passValue.includes('true') ||
690
- passValue.includes('✅') ||
691
- passValue === 'yes' ||
692
- passValue === 'passed';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
  }
694
-
695
- parsedData.overall = {
696
- grade,
697
- pass,
698
- primaryIssue: pass ? 'All sections passed successfully.' : 'Overall assessment failed.',
699
- rawContent: cleanedQaText
700
- };
701
-
702
- console.log('Found inline overall results - grade:', grade, 'pass:', pass);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
703
  } else {
704
- // Calculate overall pass from individual sections
705
- const allSectionsPassed = finalReport.title.pass &&
706
- finalReport.meta.pass &&
707
- finalReport.h1.pass &&
708
- finalReport.copy.pass;
709
-
710
- // Calculate average grade if all sections have numeric grades
711
- let averageGrade = 'N/A';
712
- const grades = [finalReport.title.grade, finalReport.meta.grade, finalReport.h1.grade, finalReport.copy.grade];
713
- const numericGrades = grades.filter(g => g !== 'N/A' && g !== undefined)
714
- .map(g => {
715
- const match = String(g).match(/(\d+(?:\.\d+)?)/);
716
- return match ? parseFloat(match[1]) : null;
717
- })
718
- .filter(g => g !== null) as number[];
719
-
720
- if (numericGrades.length === 4) {
721
- const avg = numericGrades.reduce((sum, grade) => sum + grade, 0) / numericGrades.length;
722
- averageGrade = `${avg.toFixed(2)}/100`;
723
- }
724
-
725
- parsedData.overall = {
726
- grade: averageGrade,
727
- pass: allSectionsPassed,
728
- primaryIssue: allSectionsPassed ? 'All sections passed successfully.' : 'One or more sections failed.',
729
- rawContent: cleanedQaText
730
- };
731
-
732
- console.log('Calculated overall from individual sections - pass:', allSectionsPassed, 'grade:', averageGrade);
733
  }
734
-
735
- // Update the final report with the calculated/found overall data
736
- finalReport.overall = parsedData.overall;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737
  }
 
 
 
 
 
 
 
 
 
 
 
738
 
739
- console.log('Final parsed QA data:', finalReport.overall);
740
- console.log('Setting overallPass:', finalReport.overall.pass, 'overallGrade:', finalReport.overall.grade);
741
- console.log('Additional sections captured:', Object.keys(additionalSections));
 
 
742
 
743
- return {
744
- detailedQaReport: finalReport,
745
- overallPass: finalReport.overall.pass,
746
- overallGrade: finalReport.overall.grade
747
- };
748
- } else {
749
- // Parse the new structured format
750
- return parseStructuredQaReport(cleanedQaText);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  };
753
 
754
 
 
 
1
  import { API_URL, API_TOKEN, API_USER } from '../constants';
2
  import { ApiInput, ApiResponseOutput, ProcessedResult, QaSectionResult, DetailedQaReport } from '../types';
3
 
 
66
  }
67
  console.log('Grade match result:', gradeMatch, 'Final grade:', grade);
68
 
69
+ // ROBUST PASS EXTRACTION - handles multiple formats including the actual QA Guard format
70
  let pass = false;
71
  let passMatch = null;
72
 
73
+ // Try various pass patterns in order of specificity - FIXED TO HANDLE ACTUAL QA GUARD FORMAT
74
  const passPatterns = [
75
+ // First check for the actual QA Guard format: "### **TITLE: PASS** ✅"
76
+ /###\s*\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*\s*(✅|❌)/i,
77
+ /###\s*\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*/i,
78
+ /\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*\s*(✅|❌)/i,
79
+ /\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*/i,
80
+
81
+ // Then check for traditional patterns
82
  /-\s*\*\*Pass:\*\*\s*(.*)/i, // - **Pass:** true
83
  /•\s*\*\*Pass:\*\*\s*(.*)/i, // • **Pass:** true
84
  /\*\s*\*\*Pass:\*\*\s*(.*)/i, // * **Pass:** true
 
95
  passMatch = sectionText.match(pattern);
96
  if (passMatch) {
97
  const passValue = passMatch[1].toLowerCase().trim();
98
+ pass = passValue.includes('pass') ||
99
+ passValue.includes('true') ||
100
  passValue.includes('✅') ||
101
  passValue === 'yes' ||
102
+ passValue === 'passed';
 
103
  break;
104
  }
105
  }
 
145
  }
146
  }
147
 
148
+ // ENHANCED LOGIC: If we have a grade of 100/100 and no errors, but pass is still false,
149
+ // we should override the pass status based on the grade and errors
150
+ if (grade === '100/100' && (!errors || errors.length === 0 || errors[0] === 'No errors reported.')) {
151
+ console.log('Overriding pass status: Grade is 100/100 and no errors, setting pass to true');
152
+ pass = true;
153
+ }
154
+
155
+ // Additional logic: If grade is high (80+) and no errors, likely a pass
156
+ if (grade !== 'N/A' && grade !== '0/100') {
157
+ const gradeNum = parseInt(grade.split('/')[0]);
158
+ if (gradeNum >= 80 && (!errors || errors.length === 0 || errors[0] === 'No errors reported.')) {
159
+ console.log(`Overriding pass status: Grade is ${grade} (${gradeNum}/100) and no errors, setting pass to true`);
160
+ pass = true;
161
+ }
162
+ }
163
+
164
  // ROBUST ANALYSIS/CORRECTED CONTENT EXTRACTION - handles multiple formats
165
  let corrected = 'Content analysis not available.';
166
  let contentMatch = null;
 
201
  corrected = corrected.replace(/^#\s*/, '').replace(/###\s*\*\*[^*]+\*\*/, '').trim();
202
  console.log('Content match result:', contentMatch, 'Final corrected:', corrected.substring(0, 50));
203
 
204
+ console.log('Final section result - Grade:', grade, 'Pass:', pass, 'Errors:', errors);
205
  return { grade, pass, errors, corrected };
206
  };
207
 
 
423
  * Enhanced overall section parsing that captures ALL QA Guard content
424
  */
425
  const parseEnhancedOverallSection = (sectionBlock: string): { grade: string; pass: boolean; primaryIssue: string; detailedAssessment?: string; keyStrengths?: string[]; recommendations?: string[]; explanations?: string; rawContent?: string } => {
426
+ console.log('Parsing enhanced overall section with actual QA Guard format');
427
+ console.log('Overall section preview:', sectionBlock.substring(0, 300));
428
+
429
  let grade = 'N/A';
430
+ let pass = false;
431
+ let primaryIssue = 'Overall assessment not available.';
432
+ let detailedAssessment = '';
433
+ let keyStrengths: string[] = [];
434
+ let recommendations: string[] = [];
435
+ let explanations = '';
436
 
437
+ // COMPREHENSIVE OVERALL GRADE PATTERN MATCHING
438
+ // Look for the actual QA Guard overall format: "Final Grade: 98.75/100"
439
  const overallGradePatterns = [
440
+ /Final\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Final Grade: 98.75/100
441
+ /Overall\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Grade: 98.75/100
442
+ /Total\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Total Grade: 98.75/100
443
+ /Combined\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Combined Grade: 98.75/100
444
+ /Average\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Average Grade: 98.75/100
445
+ /Mean\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Mean Grade: 98.75/100
446
+ /Composite\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Composite Grade: 98.75/100
447
+ /Final\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Final Score: 98.75/100
448
+ /Overall\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Score: 98.75/100
449
+ /Total\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Total Score: 98.75/100
450
+ /Final\s+Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Final Rating: 98.75/100
451
+ /Overall\s+Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Rating: 98.75/100
452
+ /Final\s+Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Final Mark: 98.75/100
453
+ /Overall\s+Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Mark: 98.75/100
454
+ /Final\s+Points:\s*(\d+(?:\.\d+)?)\/100/i, // Final Points: 98.75/100
455
+ /Overall\s+Points:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Points: 98.75/100
456
+ /-?\s*\*\*Final\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Grade:** 98.75/100
457
+ /-?\s*\*\*Overall\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Grade:** 98.75/100
458
+ /-?\s*\*\*Total\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Total Grade:** 98.75/100
459
+ /-?\s*\*\*Final\s+Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Score:** 98.75/100
460
+ /-?\s*\*\*Overall\s+Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Score:** 98.75/100
461
+ /-?\s*\*\*Final\s+Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Rating:** 98.75/100
462
+ /-?\s*\*\*Overall\s+Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Rating:** 98.75/100
463
+ /Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Grade: 98.75/100
464
+ /Score:\s*(\d+(?:\.\d+)?)\/100/i, // Score: 98.75/100
465
+ /Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Rating: 98.75/100
466
+ /Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Mark: 98.75/100
467
+ /Points:\s*(\d+(?:\.\d+)?)\/100/i, // Points: 98.75/100
468
  ];
469
 
470
+ let finalGradeMatch = null;
471
  for (const pattern of overallGradePatterns) {
472
+ finalGradeMatch = sectionBlock.match(pattern);
473
+ if (finalGradeMatch) {
474
+ grade = `${finalGradeMatch[1]}/100`;
475
+ console.log('Found final grade:', grade);
476
  break;
477
  }
478
  }
 
 
 
 
 
479
 
480
+ // COMPREHENSIVE OVERALL PASS STATUS PATTERN MATCHING
481
+ // Look for overall pass status: "Overall Pass: FALSE (due to META violation)"
482
  const overallPassPatterns = [
483
+ /Overall\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Pass: FALSE (due to META violation)
484
+ /Final\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Pass: FALSE (due to META violation)
485
+ /Total\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Total Pass: FALSE (due to META violation)
486
+ /Combined\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Combined Pass: FALSE (due to META violation)
487
+ /Average\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Average Pass: FALSE (due to META violation)
488
+ /Overall\s+Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Status: FALSE (due to META violation)
489
+ /Final\s+Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Status: FALSE (due to META violation)
490
+ /Overall\s+Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Result: FALSE (due to META violation)
491
+ /Final\s+Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Result: FALSE (due to META violation)
492
+ /Overall\s+Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Assessment: FALSE (due to META violation)
493
+ /Final\s+Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Assessment: FALSE (due to META violation)
494
+ /-?\s*\*\*Overall\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Overall Pass:** FALSE (due to META violation)
495
+ /-?\s*\*\*Final\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Final Pass:** FALSE (due to META violation)
496
+ /-?\s*\*\*Total\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Total Pass:** FALSE (due to META violation)
497
+ /-?\s*\*\*Overall\s+Status:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Overall Status:** FALSE (due to META violation)
498
+ /-?\s*\*\*Final\s+Status:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Final Status:** FALSE (due to META violation)
499
+ /Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Pass: FALSE (due to META violation)
500
+ /Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Status: FALSE (due to META violation)
501
+ /Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Result: FALSE (due to META violation)
502
+ /Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Assessment: FALSE (due to META violation)
 
 
 
 
503
  ];
504
 
505
+ let overallPassMatch = null;
506
  for (const pattern of overallPassPatterns) {
507
+ overallPassMatch = sectionBlock.match(pattern);
508
+ if (overallPassMatch) {
509
+ pass = overallPassMatch[1].toUpperCase() === 'TRUE' || overallPassMatch[1].toUpperCase() === 'PASS';
510
+ console.log('Found overall pass status:', pass);
 
 
 
 
511
  break;
512
  }
513
  }
 
514
 
515
+ // COMPREHENSIVE PRIMARY ISSUE PATTERN MATCHING
516
+ // Look for primary issue in the pass status explanation
517
+ const issuePatterns = [
518
+ /Overall\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Overall Pass: FALSE (due to META violation)
519
+ /Final\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Final Pass: FALSE (due to META violation)
520
+ /Total\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Total Pass: FALSE (due to META violation)
521
+ /Overall\s+Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Overall Status: FALSE (due to META violation)
522
+ /Final\s+Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Final Status: FALSE (due to META violation)
523
+ /Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Pass: FALSE (due to META violation)
524
+ /Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Status: FALSE (due to META violation)
525
+ /Result:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Result: FALSE (due to META violation)
526
+ /Assessment:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Assessment: FALSE (due to META violation)
527
+ /-?\s*\*\*Overall\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Overall Pass:** FALSE (due to META violation)
528
+ /-?\s*\*\*Final\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Final Pass:** FALSE (due to META violation)
529
+ /-?\s*\*\*Total\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Total Pass:** FALSE (due to META violation)
530
+ /-?\s*\*\*Overall\s+Status:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Overall Status:** FALSE (due to META violation)
531
+ /-?\s*\*\*Final\s+Status:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Final Status:** FALSE (due to META violation)
532
+ /-?\s*\*\*Primary\s+Issue:\*\*\s*([^\n]+)/i, // - **Primary Issue:** Some sections have violations
533
+ /-?\s*\*\*Main\s+Issue:\*\*\s*([^\n]+)/i, // - **Main Issue:** Some sections have violations
534
+ /-?\s*\*\*Key\s+Issue:\*\*\s*([^\n]+)/i, // - **Key Issue:** Some sections have violations
535
+ /-?\s*\*\*Issue:\*\*\s*([^\n]+)/i, // - **Issue:** Some sections have violations
536
+ /-?\s*\*\*Problem:\*\*\s*([^\n]+)/i, // - **Problem:** Some sections have violations
537
+ /-?\s*\*\*Concern:\*\*\s*([^\n]+)/i, // - **Concern:** Some sections have violations
538
+ /Primary\s+Issue:\s*([^\n]+)/i, // Primary Issue: Some sections have violations
539
+ /Main\s+Issue:\s*([^\n]+)/i, // Main Issue: Some sections have violations
540
+ /Key\s+Issue:\s*([^\n]+)/i, // Key Issue: Some sections have violations
541
+ /Issue:\s*([^\n]+)/i, // Issue: Some sections have violations
542
+ /Problem:\s*([^\n]+)/i, // Problem: Some sections have violations
543
+ /Concern:\s*([^\n]+)/i, // Concern: Some sections have violations
544
+ ];
545
 
546
+ let issueMatch = null;
547
+ for (const pattern of issuePatterns) {
548
+ issueMatch = sectionBlock.match(pattern);
549
+ if (issueMatch) {
550
+ primaryIssue = issueMatch[1].trim();
551
+ console.log('Found primary issue:', primaryIssue);
552
+ break;
 
 
 
 
553
  }
554
  }
 
 
 
555
 
556
+ // COMPREHENSIVE DETAILED BREAKDOWN PATTERN MATCHING
557
+ // Look for detailed breakdown sections
558
+ const breakdownPatterns = [
559
+ /##\s+DETAILED\s+BREAKDOWN[^#]*/i, // ## DETAILED BREAKDOWN
560
+ /##\s+BREAKDOWN[^#]*/i, // ## BREAKDOWN
561
+ /##\s+ANALYSIS[^#]*/i, // ## ANALYSIS
562
+ /##\s+ASSESSMENT[^#]*/i, // ## ASSESSMENT
563
+ /##\s+EVALUATION[^#]*/i, // ## EVALUATION
564
+ /##\s+REVIEW[^#]*/i, // ## REVIEW
565
+ /##\s+SUMMARY[^#]*/i, // ## SUMMARY
566
+ /##\s+DETAILS[^#]*/i, // ## DETAILS
567
+ /##\s+EXPLANATION[^#]*/i, // ## EXPLANATION
568
+ /##\s+COMMENTS[^#]*/i, // ## COMMENTS
569
+ /##\s+NOTES[^#]*/i, // ## NOTES
570
+ /###\s+DETAILED\s+BREAKDOWN[^#]*/i, // ### DETAILED BREAKDOWN
571
+ /###\s+BREAKDOWN[^#]*/i, // ### BREAKDOWN
572
+ /###\s+ANALYSIS[^#]*/i, // ### ANALYSIS
573
+ /###\s+ASSESSMENT[^#]*/i, // ### ASSESSMENT
574
+ /###\s+EVALUATION[^#]*/i, // ### EVALUATION
575
+ /###\s+REVIEW[^#]*/i, // ### REVIEW
576
+ /###\s+SUMMARY[^#]*/i, // ### SUMMARY
577
+ /###\s+DETAILS[^#]*/i, // ### DETAILS
578
+ /###\s+EXPLANATION[^#]*/i, // ### EXPLANATION
579
+ /###\s+COMMENTS[^#]*/i, // ### COMMENTS
580
+ /###\s+NOTES[^#]*/i, // ### NOTES
581
+ ];
582
 
583
+ let breakdownMatch = null;
584
+ for (const pattern of breakdownPatterns) {
585
+ breakdownMatch = sectionBlock.match(pattern);
586
+ if (breakdownMatch) {
587
+ detailedAssessment = breakdownMatch[0];
588
+ console.log('Found detailed breakdown');
589
+ break;
590
+ }
591
+ }
592
+
593
+ // ENHANCED KEY STRENGTHS AND RECOMMENDATIONS EXTRACTION
594
+ // Extract key strengths and recommendations from the overall assessment
595
+ const lowerSection = sectionBlock.toLowerCase();
596
 
597
  // Extract key strengths
598
+ if (lowerSection.includes('compliance') && lowerSection.includes('requirements')) {
599
+ keyStrengths.push('Overall compliance with requirements');
600
+ }
601
+ if (grade !== 'N/A' && parseFloat(grade.split('/')[0]) >= 80) {
602
+ keyStrengths.push('High overall grade achieved');
603
+ }
604
+ if (lowerSection.includes('successful') || lowerSection.includes('approved')) {
605
+ keyStrengths.push('Overall assessment successful');
606
+ }
607
+ if (lowerSection.includes('meets') && lowerSection.includes('standards')) {
608
+ keyStrengths.push('Meets overall standards');
609
+ }
610
+ if (lowerSection.includes('satisfies') && lowerSection.includes('criteria')) {
611
+ keyStrengths.push('Satisfies overall criteria');
612
+ }
613
+ if (lowerSection.includes('valid') || lowerSection.includes('correct')) {
614
+ keyStrengths.push('Overall content validation passed');
615
+ }
616
 
617
  // Extract recommendations
618
+ if (lowerSection.includes('violation') || lowerSection.includes('fail')) {
619
+ recommendations.push('Address identified violations');
620
+ }
621
+ if (lowerSection.includes('correction') || lowerSection.includes('fix')) {
622
+ recommendations.push('Implement suggested corrections');
623
+ }
624
+ if (lowerSection.includes('improve') || lowerSection.includes('enhance')) {
625
+ recommendations.push('Improve overall content quality');
626
+ }
627
+ if (lowerSection.includes('adjust') || lowerSection.includes('modify')) {
628
+ recommendations.push('Adjust content to meet requirements');
629
+ }
630
+ if (lowerSection.includes('review') && lowerSection.includes('carefully')) {
631
+ recommendations.push('Review content carefully');
632
+ }
633
+ if (lowerSection.includes('consider') && lowerSection.includes('changes')) {
634
+ recommendations.push('Consider suggested changes');
635
+ }
636
 
637
+ // FALLBACK PATTERN MATCHING
638
+ // If no grade found, try alternative patterns
639
+ if (grade === 'N/A') {
640
+ const fallbackGradePatterns = [
641
+ /Grade:\s*(\d+(?:\.\d+)?)\/100/i,
642
+ /Score:\s*(\d+(?:\.\d+)?)\/100/i,
643
+ /Rating:\s*(\d+(?:\.\d+)?)\/100/i,
644
+ /Mark:\s*(\d+(?:\.\d+)?)\/100/i,
645
+ /Points:\s*(\d+(?:\.\d+)?)\/100/i,
646
+ /(\d+(?:\.\d+)?)\/100/i,
647
+ ];
648
+
649
+ for (const pattern of fallbackGradePatterns) {
650
+ const match = sectionBlock.match(pattern);
651
+ if (match) {
652
+ grade = `${match[1]}/100`;
653
+ console.log('Found fallback grade:', grade);
654
+ break;
655
+ }
656
+ }
657
+ }
658
 
659
+ // INFER PASS STATUS FROM GRADE IF NOT DETERMINED
660
+ if (grade !== 'N/A' && !overallPassMatch) {
661
+ const gradeNum = parseFloat(grade.split('/')[0]);
662
+ pass = gradeNum >= 80;
663
+ console.log('Inferred overall pass status from grade:', pass);
664
+ }
665
+
666
+ // FINAL PRIMARY ISSUE DETERMINATION
667
+ // If still no primary issue, generate one based on pass status
668
+ if (primaryIssue === 'Overall assessment not available.') {
669
+ if (pass) {
670
+ primaryIssue = 'All sections meet requirements';
671
+ } else {
672
+ primaryIssue = 'Some sections have violations';
673
+ }
674
+ }
675
+
676
+ console.log('Final overall result - Grade:', grade, 'Pass:', pass, 'Issue:', primaryIssue);
677
+
678
+ return {
679
+ grade,
680
+ pass,
681
  primaryIssue,
682
+ detailedAssessment: detailedAssessment || undefined,
683
+ keyStrengths: keyStrengths.length > 0 ? keyStrengths : undefined,
684
+ recommendations: recommendations.length > 0 ? recommendations : undefined,
685
+ explanations: explanations || undefined,
686
  rawContent: sectionBlock
687
  };
688
  };
 
728
  return { detailedQaReport: defaultReport, overallPass: false, overallGrade: 'N/A' };
729
  }
730
 
731
+ console.log('Enhanced QA parsing - input text preview:', cleanedQaText.substring(0, 500));
732
+
733
+ // COMPLETELY REWRITTEN TO HANDLE ACTUAL QA GUARD FORMAT
734
+ // The QA Guard uses formats like: "## **TITLE GRADE: 100/100 ✅ PASS**"
735
+
736
+ const parsedData: Partial<DetailedQaReport> = {};
737
+ const additionalSections: { [sectionName: string]: { content: string; type: 'assessment' | 'strengths' | 'recommendations' | 'explanations' | 'other'; } } = {};
738
+
739
+ // Enhanced section splitting to handle the actual QA Guard format
740
+ let sections: string[] = [];
741
+
742
+ // First, try to remove any leading overall evaluation header that might interfere with section splitting
743
+ const contentAfterOverallHeader = cleanedQaText.replace(/^(#\s*FINAL\s+QUALITY\s+ASSURANCE\s+EVALUATION|##\s*Section\s+Grades)\s*/i, '').trim();
744
+
745
+ // Try multiple splitting strategies for robustness
746
+ // Order matters: more specific patterns should come first
747
+ const sectionHeaderPatterns = [
748
+ // Highly specific: ### **SECTION NAME** - GRADE: X/100 ✅ PASS
749
+ /(?=(?:##|###)\s*\*\*([^*]+)\*\*\s*-\s*GRADE:)/g,
750
+ // Highly specific: ### **SECTION NAME: PASS** ✅
751
+ /(?=(?:##|###)\s*\*\*([^*]+):\s*(?:PASS|FAIL)\*\*\s*(?:✅|❌))/g,
752
+ // Specific: ## **SECTION NAME GRADE:**
753
+ /(?=(?:##|###)\s*\*\*([^*]+)\s+GRADE:)/g,
754
+ // Specific: ## **SECTION NAME** (with optional pass/fail indicator)
755
+ /(?=(?:##|###)\s*\*\*([^*]+)\*\*(?:\s*(?:✅|❌|PASS|FAIL))?)/g,
756
+ // Generic: ## Section Name
757
+ /(?=(?:##|###)\s*[^#\n]*)/g,
758
+ ];
759
+
760
+ for (const pattern of sectionHeaderPatterns) {
761
+ sections = contentAfterOverallHeader.split(pattern).filter(Boolean);
762
+ if (sections.length > 1) { // If we found more than one section, this split was successful
763
+ console.log(`Using successful split pattern: ${pattern}`);
764
+ break;
765
+ }
766
+ }
767
+
768
+ // Fallback to paragraph splitting if no suitable header pattern was found
769
+ if (sections.length <= 1) {
770
+ sections = contentAfterOverallHeader.split(/\n\n+/).filter(section => section.trim().length > 20);
771
+ console.log('Using paragraph-based parsing as fallback (no header pattern matched)');
772
+ }
773
+
774
+ // Ensure sections are not empty after splitting
775
+ sections = sections.filter(s => s.trim().length > 5); // Minimum length to be considered a valid section
776
+
777
+ console.log(`Found ${sections.length} sections to parse`);
778
+ sections.forEach((section, index) => {
779
+ console.log(`Section ${index} preview:`, section.substring(0, 150));
780
+ });
781
+
782
+ // Parse each section with enhanced logic
783
+ sections.forEach((sectionBlock, index) => {
784
+ const lines = sectionBlock.trim().split('\n');
785
+ const headerRaw = lines[0]?.trim() || '';
786
+ const header = headerRaw.toLowerCase();
787
+
788
+ console.log(`Processing section ${index} with header:`, headerRaw);
789
+
790
+ let sectionType = ''; // Must be reset for each block
791
+ let sectionData: QaSectionResult | null = null;
792
+
793
+ // New, more robust section identification logic
794
+ const headerForTypeCheck = header
795
+ .replace(/^(#+\s*|\*\*)/, '')
796
+ .replace(/[:*]/g, '')
797
+ .replace(/\s*-\s*grade:.*/, '') // also strip grade info
798
+ .replace(/\s*(✅|❌|pass|fail).*/, '') // and status info
799
+ .trim();
800
+
801
+ const words = headerForTypeCheck.split(/\s+/).filter(Boolean);
802
+
803
+ // If the normalized header is just ONE clean word, it's a primary section
804
+ if (words.length === 1) {
805
+ const typeWord = words[0];
806
+ if (typeWord === 'title') sectionType = 'title';
807
+ else if (typeWord === 'meta') sectionType = 'meta';
808
+ else if (typeWord === 'h1') sectionType = 'h1';
809
+ else if (typeWord === 'copy') sectionType = 'copy';
810
+ else if (['overall', 'final', 'assessment'].includes(typeWord)) sectionType = 'overall';
811
+ }
812
+
813
+ // If not identified, check for specific multi-word headers for 'overall'
814
+ if (!sectionType) {
815
+ if (headerForTypeCheck.startsWith('overall assessment') || headerForTypeCheck.startsWith('final assessment')) {
816
+ sectionType = 'overall';
817
+ }
818
+ }
819
 
820
+ // Route to the correct parser or handle as an additional section
821
+ if (sectionType === 'title') {
822
+ console.log('Identified as TITLE section');
823
+ sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
824
+ if (sectionData) parsedData.title = sectionData;
825
+ } else if (sectionType === 'meta') {
826
+ console.log('Identified as META section');
827
+ sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
828
+ if (sectionData) parsedData.meta = sectionData;
829
+ } else if (sectionType === 'h1') {
830
+ console.log('Identified as H1 section');
831
+ sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
832
+ if (sectionData) parsedData.h1 = sectionData;
833
+ } else if (sectionType === 'copy') {
834
+ console.log('Identified as COPY section');
835
+ sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
836
+ if (sectionData) parsedData.copy = sectionData;
837
+ } else if (sectionType === 'overall') {
838
+ console.log('Identified as OVERALL section');
839
+ const enhancedOverall = parseEnhancedOverallSection(sectionBlock);
840
+ parsedData.overall = enhancedOverall;
841
+ } else if (header.includes('grades by section') || header.includes('section grades')) {
842
+ console.log('Identified as introductory section, skipping.');
843
+ return;
844
  } else {
845
+ // Additional sections logic remains here
846
+ console.log('Identified as additional section');
847
+ let displayName = headerRaw.replace(/^[#*\s-]+/g, '').trim();
848
+ // ... (existing additional section mapping logic) ...
849
+ additionalSections[displayName] = {
850
+ content: sectionBlock,
851
+ type: determineSectionType(sectionBlock)
852
+ };
853
  }
854
+ });
855
+
856
+ // If no sections were found, try to extract from the complete text
857
+ if (Object.keys(parsedData).length === 0) {
858
+ console.log('No sections found, attempting full-text extraction');
859
 
860
+ // Try to extract grades and pass status from the raw text
861
+ const titleMatch = cleanedQaText.match(/TITLE[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
862
+ const metaMatch = cleanedQaText.match(/META[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
863
+ const h1Match = cleanedQaText.match(/H1[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
864
+ const copyMatch = cleanedQaText.match(/COPY[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
865
 
866
+ if (titleMatch) {
867
+ parsedData.title = {
868
+ grade: `${titleMatch[1]}/100`,
869
+ pass: titleMatch[2] === '✅' || titleMatch[2].toUpperCase() === 'PASS',
870
+ errors: titleMatch[2] === '✅' || titleMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
871
+ corrected: 'Content analysis extracted from full report',
872
+ rawContent: cleanedQaText
873
+ };
874
+ console.log('Extracted TITLE data:', parsedData.title);
875
  }
876
 
877
+ if (metaMatch) {
878
+ parsedData.meta = {
879
+ grade: `${metaMatch[1]}/100`,
880
+ pass: metaMatch[2] === '✅' || metaMatch[2].toUpperCase() === 'PASS',
881
+ errors: metaMatch[2] === '✅' || metaMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
882
+ corrected: 'Content analysis extracted from full report',
883
+ rawContent: cleanedQaText
884
+ };
885
+ console.log('Extracted META data:', parsedData.meta);
886
+ }
887
+
888
+ if (h1Match) {
889
+ parsedData.h1 = {
890
+ grade: `${h1Match[1]}/100`,
891
+ pass: h1Match[2] === '✅' || h1Match[2].toUpperCase() === 'PASS',
892
+ errors: h1Match[2] === '✅' || h1Match[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
893
+ corrected: 'Content analysis extracted from full report',
894
+ rawContent: cleanedQaText
895
+ };
896
+ console.log('Extracted H1 data:', parsedData.h1);
897
+ }
898
+
899
+ if (copyMatch) {
900
+ parsedData.copy = {
901
+ grade: `${copyMatch[1]}/100`,
902
+ pass: copyMatch[2] === '' || copyMatch[2].toUpperCase() === 'PASS',
903
+ errors: copyMatch[2] === '✅' || copyMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
904
+ corrected: 'Content analysis extracted from full report',
905
+ rawContent: cleanedQaText
906
+ };
907
+ console.log('Extracted COPY data:', parsedData.copy);
908
+ }
909
+
910
+ // Extract overall assessment
911
+ const overallMatch = cleanedQaText.match(/(?:OVERALL|Final).*?(\d+)\/100/i);
912
+ if (overallMatch) {
913
+ const overallGrade = parseInt(overallMatch[1]);
914
+ parsedData.overall = {
915
+ grade: `${overallGrade}/100`,
916
+ pass: overallGrade >= 80,
917
+ primaryIssue: overallGrade >= 80 ? 'All sections meet requirements' : 'Some violations detected',
918
+ rawContent: cleanedQaText
919
+ };
920
+ console.log('Extracted OVERALL data:', parsedData.overall);
921
+ }
922
+ }
 
 
 
 
 
 
923
 
924
+ const finalReport: DetailedQaReport = {
925
+ title: parsedData.title || { ...defaultSection, errors: ['Title section not found in QA response'] },
926
+ meta: parsedData.meta || { ...defaultSection, errors: ['Meta section not found in QA response'] },
927
+ h1: parsedData.h1 || { ...defaultSection, errors: ['H1 section not found in QA response'] },
928
+ copy: parsedData.copy || { ...defaultSection, errors: ['Copy section not found in QA response'] },
929
+ overall: parsedData.overall || { grade: 'N/A', pass: false, primaryIssue: 'Overall assessment not found in QA response' },
930
+ additionalSections: Object.keys(additionalSections).length > 0 ? additionalSections : undefined,
931
+ completeRawReport: qaText
932
+ };
933
+
934
+ // Calculate overall pass/grade from individual sections if overall not found
935
+ if (!parsedData.overall && (parsedData.title || parsedData.meta || parsedData.h1 || parsedData.copy)) {
936
+ const validSections = [parsedData.title, parsedData.meta, parsedData.h1, parsedData.copy].filter(Boolean);
937
+ const allPass = validSections.every(section => section?.pass);
938
+ const grades = validSections.map(section => {
939
+ if (section?.grade && section.grade !== 'N/A') {
940
+ const match = section.grade.match(/(\d+)/);
941
+ return match ? parseInt(match[1]) : 0;
942
  }
943
+ return 0;
944
+ }).filter(g => g > 0);
945
+
946
+ const avgGrade = grades.length > 0 ? Math.round(grades.reduce((a, b) => a + b, 0) / grades.length) : 0;
947
+
948
+ finalReport.overall = {
949
+ grade: avgGrade > 0 ? `${avgGrade}/100` : 'N/A',
950
+ pass: allPass && avgGrade >= 80,
951
+ primaryIssue: allPass ? 'All sections passed' : 'Some sections have violations',
952
+ rawContent: cleanedQaText
953
+ };
954
+
955
+ console.log('Calculated overall from sections:', finalReport.overall);
956
+ }
957
+
958
+ console.log('Final QA parsing result:', {
959
+ title: finalReport.title?.grade,
960
+ meta: finalReport.meta?.grade,
961
+ h1: finalReport.h1?.grade,
962
+ copy: finalReport.copy?.grade,
963
+ overall: finalReport.overall?.grade,
964
+ overallPass: finalReport.overall?.pass
965
+ });
966
+
967
+ return {
968
+ detailedQaReport: finalReport,
969
+ overallPass: finalReport.overall.pass,
970
+ overallGrade: finalReport.overall.grade
971
+ };
972
+ };
973
 
974
+ /**
975
+ * Parses a single section of the QA report (e.g., TITLE, H1) based on the actual QA Guard format.
976
+ * This is a more robust parser that handles variations in header format and content.
977
+ */
978
+ const parseActualQAGuardSection = (sectionBlock: string, sectionType: string): QaSectionResult | null => {
979
+ console.log(`Parsing ${sectionType} section with actual QA Guard format`);
980
+ console.log('Section block preview:', sectionBlock.substring(0, 200));
981
+
982
+ // Extract grade and pass status from the actual QA Guard format
983
  let grade = 'N/A';
984
+ let pass = false;
985
+ let errors: string[] = ['No errors reported.'];
986
+ let corrected = 'Content analysis not available.';
987
+ let detailedAssessment = '';
988
+ let keyStrengths: string[] = [];
989
+ let recommendations: string[] = [];
990
+ let explanations = '';
991
+
992
+ // COMPREHENSIVE HEADER PATTERN MATCHING
993
+ // Look for the actual QA Guard format: "### **TITLE** ✅ PASS" or "### **TITLE** ❌ FAIL"
994
+ // Also handle variations like "## **TITLE: PASS** ✅" or "## **TITLE: FAIL** ❌"
995
+ const headerPatterns = [
996
+ /###\s*\*\*([^*]+)\*\*\s*(✅|❌)\s*(PASS|FAIL)/i, // ### **TITLE** ✅ PASS
997
+ /##\s*\*\*([^*]+):\s*(PASS|FAIL)\*\*\s*(✅|❌)/i, // ## **TITLE: PASS** ✅
998
+ /##\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)\s*(✅|❌)/i, // ## **TITLE** PASS ✅
999
+ /###\s*\*\*([^*]+):\s*(PASS|FAIL)\*\*\s*(✅|❌)/i, // ### **TITLE: PASS** ✅
1000
+ /##\s*\*\*([^*]+)\*\*\s*(✅|❌)\s*(PASS|FAIL)/i, // ## **TITLE** ✅ PASS
1001
+ /###\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)/i, // ### **TITLE** PASS
1002
+ /##\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)/i, // ## **TITLE** PASS
1003
+ /###\s*\*\*([^*]+):\s*(PASS|FAIL)/i, // ### **TITLE: PASS**
1004
+ /##\s*\*\*([^*]+):\s*(PASS|FAIL)/i, // ## **TITLE: PASS**
1005
+ /###\s*([^*]+)\s*(✅|❌)\s*(PASS|FAIL)/i, // ### TITLE ✅ PASS
1006
+ /##\s*([^*]+)\s*(✅|❌)\s*(PASS|FAIL)/i, // ## TITLE ✅ PASS
1007
+ /###\s*([^*]+):\s*(PASS|FAIL)\s*(✅|❌)/i, // ### TITLE: PASS ✅
1008
+ /##\s*([^*]+):\s*(PASS|FAIL)\s*(✅|❌)/i, // ## TITLE: PASS ✅
1009
+ /###\s*([^*]+)\s*(PASS|FAIL)/i, // ### TITLE PASS
1010
+ /##\s*([^*]+)\s*(PASS|FAIL)/i, // ## TITLE PASS
1011
+ /###\s*([^*]+):\s*(PASS|FAIL)/i, // ### TITLE: PASS
1012
+ /##\s*([^*]+):\s*(PASS|FAIL)/i, // ## TITLE: PASS
1013
+ ];
1014
+
1015
+ let headerMatch = null;
1016
+ for (const pattern of headerPatterns) {
1017
+ headerMatch = sectionBlock.match(pattern);
1018
+ if (headerMatch) {
1019
+ console.log('Found QA Guard header format:', headerMatch[0]);
1020
+ // Determine pass status from various indicators in the match
1021
+ const passIndicators = [headerMatch[2], headerMatch[3]].filter(Boolean);
1022
+ pass = passIndicators.some(indicator =>
1023
+ indicator === '✅' || indicator.toUpperCase() === 'PASS'
1024
+ );
1025
+ break;
1026
+ }
1027
+ }
1028
+
1029
+ // COMPREHENSIVE GRADE PATTERN MATCHING
1030
+ // Look for grade in various formats: "- **Grade:** 100/100", "Grade: 100/100", etc.
1031
+ const gradePatterns = [
1032
+ /-?\s*\*\*Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Grade:** 100/100
1033
+ /-?\s*\*\*Grade\*\*:\s*(\d+(?:\.\d+)?)\/100/i, // - **Grade**: 100/100
1034
+ /-?\s*Grade:\s*(\d+(?:\.\d+)?)\/100/i, // - Grade: 100/100
1035
+ /-?\s*Grade\s*:\s*(\d+(?:\.\d+)?)\/100/i, // - Grade : 100/100
1036
+ /\*\*Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // **Grade:** 100/100
1037
+ /\*\*Grade\*\*:\s*(\d+(?:\.\d+)?)\/100/i, // **Grade**: 100/100
1038
+ /Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Grade: 100/100
1039
+ /Grade\s*:\s*(\d+(?:\.\d+)?)\/100/i, // Grade : 100/100
1040
+ /-?\s*\*\*Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Score:** 100/100
1041
+ /-?\s*\*\*Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Rating:** 100/100
1042
+ /-?\s*\*\*Mark:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Mark:** 100/100
1043
+ /-?\s*\*\*Points:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Points:** 100/100
1044
+ /-?\s*\*\*(\d+(?:\.\d+)?)\/100\s*points?\*\*/i, // - **100/100 points**
1045
+ /-?\s*(\d+(?:\.\d+)?)\/100\s*points?/i, // - 100/100 points
1046
+ /-?\s*\*\*(\d+(?:\.\d+)?)\/100\*\*/i, // - **100/100**
1047
+ /-?\s*(\d+(?:\.\d+)?)\/100/i, // - 100/100
1048
+ /\*\*(\d+(?:\.\d+)?)\/100\*\*/i, // **100/100**
1049
+ /(\d+(?:\.\d+)?)\/100/i, // 100/100
1050
+ ];
1051
+
1052
+ let gradeMatch = null;
1053
+ for (const pattern of gradePatterns) {
1054
+ gradeMatch = sectionBlock.match(pattern);
1055
+ if (gradeMatch) {
1056
+ grade = `${gradeMatch[1]}/100`;
1057
+ console.log('Found grade:', grade);
1058
+ break;
1059
+ }
1060
  }
1061
+
1062
+ // COMPREHENSIVE COMPLIANCE PATTERN MATCHING
1063
+ // Look for compliance status in various formats
1064
+ const compliancePatterns = [
1065
+ /-?\s*\*\*Compliance:\*\*\s*([^\n]+)/i, // - **Compliance:** Full compliance
1066
+ /-?\s*\*\*Status:\*\*\s*([^\n]+)/i, // - **Status:** Passed
1067
+ /-?\s*\*\*Result:\*\*\s*([^\n]+)/i, // - **Result:** Successful
1068
+ /-?\s*\*\*Assessment:\*\*\s*([^\n]+)/i, // - **Assessment:** Compliant
1069
+ /-?\s*\*\*Evaluation:\*\*\s*([^\n]+)/i, // - **Evaluation:** Pass
1070
+ /-?\s*\*\*Check:\*\*\s*([^\n]+)/i, // - **Check:** OK
1071
+ /-?\s*\*\*Verification:\*\*\s*([^\n]+)/i, // - **Verification:** Valid
1072
+ /-?\s*\*\*Review:\*\*\s*([^\n]+)/i, // - **Review:** Approved
1073
+ /-?\s*\*\*Analysis:\*\*\s*([^\n]+)/i, // - **Analysis:** Compliant
1074
+ /-?\s*\*\*Summary:\*\*\s*([^\n]+)/i, // - **Summary:** Pass
1075
+ ];
1076
+
1077
+ let complianceMatch = null;
1078
+ for (const pattern of compliancePatterns) {
1079
+ complianceMatch = sectionBlock.match(pattern);
1080
+ if (complianceMatch) {
1081
+ const compliance = complianceMatch[1].trim();
1082
+ console.log('Found compliance:', compliance);
1083
+
1084
+ // Determine pass status from compliance text
1085
+ const failIndicators = ['violation', 'fail', 'error', 'non-compliant', 'rejected', 'invalid', 'incorrect', 'missing', 'below', 'above', 'out of range'];
1086
+ const passIndicators = ['compliance', 'pass', 'success', 'valid', 'correct', 'approved', 'compliant', 'within range', 'meets', 'satisfies'];
1087
+
1088
+ const lowerCompliance = compliance.toLowerCase();
1089
+ if (failIndicators.some(indicator => lowerCompliance.includes(indicator))) {
1090
+ pass = false;
1091
+ } else if (passIndicators.some(indicator => lowerCompliance.includes(indicator))) {
1092
+ pass = true;
1093
+ }
1094
+ break;
1095
+ }
1096
+ }
1097
+
1098
+ // COMPREHENSIVE ANALYSIS PATTERN MATCHING
1099
+ // Look for analysis content in various formats
1100
+ const analysisPatterns = [
1101
+ /-?\s*\*\*Analysis:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Analysis:** Detailed text
1102
+ /-?\s*\*\*Review:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Review:** Detailed text
1103
+ /-?\s*\*\*Assessment:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Assessment:** Detailed text
1104
+ /-?\s*\*\*Evaluation:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Evaluation:** Detailed text
1105
+ /-?\s*\*\*Summary:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Summary:** Detailed text
1106
+ /-?\s*\*\*Details:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Details:** Detailed text
1107
+ /-?\s*\*\*Breakdown:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Breakdown:** Detailed text
1108
+ /-?\s*\*\*Explanation:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Explanation:** Detailed text
1109
+ /-?\s*\*\*Comments:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Comments:** Detailed text
1110
+ /-?\s*\*\*Notes:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Notes:** Detailed text
1111
+ ];
1112
+
1113
+ let analysisMatch = null;
1114
+ for (const pattern of analysisPatterns) {
1115
+ analysisMatch = sectionBlock.match(pattern);
1116
+ if (analysisMatch) {
1117
+ detailedAssessment = analysisMatch[1].trim();
1118
+ console.log('Found analysis:', detailedAssessment.substring(0, 100));
1119
+ break;
1120
+ }
1121
  }
1122
+
1123
+ // COMPREHENSIVE ERROR PATTERN MATCHING
1124
+ // Look for error information in various formats
1125
+ const errorPatterns = [
1126
+ /-?\s*\*\*Error:\*\*\s*([^\n]+)/i, // - **Error:** Error message
1127
+ /-?\s*\*\*Errors:\*\*\s*([^\n]+)/i, // - **Errors:** Error messages
1128
+ /-?\s*\*\*Issue:\*\*\s*([^\n]+)/i, // - **Issue:** Issue description
1129
+ /-?\s*\*\*Issues:\*\*\s*([^\n]+)/i, // - **Issues:** Issue descriptions
1130
+ /-?\s*\*\*Problem:\*\*\s*([^\n]+)/i, // - **Problem:** Problem description
1131
+ /-?\s*\*\*Problems:\*\*\s*([^\n]+)/i, // - **Problems:** Problem descriptions
1132
+ /-?\s*\*\*Violation:\*\*\s*([^\n]+)/i, // - **Violation:** Violation details
1133
+ /-?\s*\*\*Violations:\*\*\s*([^\n]+)/i, // - **Violations:** Violation details
1134
+ /-?\s*\*\*Warning:\*\*\s*([^\n]+)/i, // - **Warning:** Warning message
1135
+ /-?\s*\*\*Warnings:\*\*\s*([^\n]+)/i, // - **Warnings:** Warning messages
1136
+ /-?\s*\*\*Concern:\*\*\s*([^\n]+)/i, // - **Concern:** Concern details
1137
+ /-?\s*\*\*Concerns:\*\*\s*([^\n]+)/i, // - **Concerns:** Concern details
1138
+ ];
1139
+
1140
+ let errorMatch = null;
1141
+ for (const pattern of errorPatterns) {
1142
+ errorMatch = sectionBlock.match(pattern);
1143
+ if (errorMatch) {
1144
+ errors = [errorMatch[1].trim()];
1145
+ console.log('Found error:', errors[0]);
1146
+ break;
1147
+ }
1148
+ }
1149
+
1150
+ // ENHANCED: Look for multi-line violation lists
1151
+ const violationsMatch = sectionBlock.match(/-?\s*\*\*(?:Structure|Major|Minor)\s+violations?\*\*:\s*([\s\S]*?)(?=\n-?\s*\*\*|\n\n|---|$)/i);
1152
+ if (violationsMatch) {
1153
+ const violationText = violationsMatch[1].trim();
1154
+ // Split by lines that start with the violation marker
1155
+ const violationErrors = violationText.split(/\n\s*(?:-|\d+\.)\s*(?:❌|\⚠️)?\s*\*\*/g)
1156
+ .map(line => {
1157
+ // Clean up the line to get the core violation text
1158
+ return line.replace(/MAJOR VIOLATION \([^)]+\):/, '')
1159
+ .replace(/MINOR VIOLATION \([^)]+\):/, '')
1160
+ .replace(/\*\*$/, '')
1161
+ .trim();
1162
+ })
1163
+ .filter(line => line.length > 10); // Filter out empty or trivial lines
1164
+
1165
+ if (violationErrors.length > 0) {
1166
+ if (errors[0] === 'No errors reported.') {
1167
+ errors = violationErrors;
1168
  } else {
1169
+ // Prepend violation details to any existing errors
1170
+ errors = [...violationErrors, ...errors];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1171
  }
1172
+ console.log('Found detailed violation errors:', violationErrors);
1173
+ }
1174
+ }
1175
+
1176
+ // COMPREHENSIVE CORRECTED CONTENT PATTERN MATCHING
1177
+ // Look for corrected content in various formats
1178
+ const correctedPatterns = [
1179
+ /\*\*CORRECTED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **CORRECTED META:**\n```\ncontent\n```
1180
+ /\*\*CORRECTED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **CORRECTED META:**\ncontent
1181
+ /\*\*FIXED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **FIXED META:**\n```\ncontent\n```
1182
+ /\*\*FIXED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **FIXED META:**\ncontent
1183
+ /\*\*REVISED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **REVISED META:**\n```\ncontent\n```
1184
+ /\*\*REVISED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **REVISED META:**\ncontent
1185
+ /\*\*UPDATED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **UPDATED META:**\n```\ncontent\n```
1186
+ /\*\*UPDATED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **UPDATED META:**\ncontent
1187
+ /\*\*SUGGESTED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **SUGGESTED META:**\n```\ncontent\n```
1188
+ /\*\*SUGGESTED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **SUGGESTED META:**\ncontent
1189
+ /\*\*RECOMMENDED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **RECOMMENDED META:**\n```\ncontent\n```
1190
+ /\*\*RECOMMENDED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **RECOMMENDED META:**\ncontent
1191
+ /-?\s*\*\*Corrected:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Corrected:** content
1192
+ /-?\s*\*\*Fixed:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Fixed:** content
1193
+ /-?\s*\*\*Revised:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Revised:** content
1194
+ /-?\s*\*\*Updated:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Updated:** content
1195
+ /-?\s*\*\*Suggested:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Suggested:** content
1196
+ /-?\s*\*\*Recommended:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Recommended:** content
1197
+ ];
1198
+
1199
+ let correctedMatch = null;
1200
+ for (const pattern of correctedPatterns) {
1201
+ correctedMatch = sectionBlock.match(pattern);
1202
+ if (correctedMatch) {
1203
+ corrected = correctedMatch[1].trim();
1204
+ console.log('Found corrected content:', corrected.substring(0, 100));
1205
+ break;
1206
  }
1207
+ }
1208
+
1209
+ // ENHANCED KEY STRENGTHS AND RECOMMENDATIONS EXTRACTION
1210
+ // Extract key strengths and recommendations from the analysis and compliance text
1211
+ if (detailedAssessment) {
1212
+ // Look for positive indicators in analysis
1213
+ const positiveIndicators = [
1214
+ 'compliance', 'compliant', 'proper', '✓', 'check', 'valid', 'correct', 'appropriate',
1215
+ 'meets', 'satisfies', 'within range', 'successful', 'approved', 'pass', 'good', 'excellent',
1216
+ 'optimal', 'ideal', 'perfect', 'complete', 'thorough', 'comprehensive', 'effective'
1217
+ ];
1218
 
1219
+ const negativeIndicators = [
1220
+ 'violation', 'error', 'fail', 'problem', 'issue', 'concern', 'warning', 'missing',
1221
+ 'below', 'above', 'out of range', 'incorrect', 'invalid', 'non-compliant', 'rejected',
1222
+ 'insufficient', 'inadequate', 'poor', 'weak', 'deficient'
1223
+ ];
1224
 
1225
+ const lowerAnalysis = detailedAssessment.toLowerCase();
1226
+
1227
+ // Extract key strengths
1228
+ if (positiveIndicators.some(indicator => lowerAnalysis.includes(indicator))) {
1229
+ keyStrengths.push('Meets compliance requirements');
1230
+ }
1231
+ if (lowerAnalysis.includes('keyword') && (lowerAnalysis.includes('included') || lowerAnalysis.includes('present'))) {
1232
+ keyStrengths.push('Proper keyword integration');
1233
+ }
1234
+ if (lowerAnalysis.includes('tone') && lowerAnalysis.includes('appropriate')) {
1235
+ keyStrengths.push('Appropriate tone maintained');
1236
+ }
1237
+ if (lowerAnalysis.includes('character') && lowerAnalysis.includes('within')) {
1238
+ keyStrengths.push('Character count within requirements');
1239
+ }
1240
+ if (lowerAnalysis.includes('length') && lowerAnalysis.includes('✓')) {
1241
+ keyStrengths.push('Length requirements met');
1242
+ }
1243
+ if (lowerAnalysis.includes('structure') && lowerAnalysis.includes('proper')) {
1244
+ keyStrengths.push('Proper structure maintained');
1245
+ }
1246
+
1247
+ // Extract recommendations
1248
+ if (negativeIndicators.some(indicator => lowerAnalysis.includes(indicator))) {
1249
+ recommendations.push('Address identified violations');
1250
+ }
1251
+ if (lowerAnalysis.includes('character') && (lowerAnalysis.includes('count') || lowerAnalysis.includes('length'))) {
1252
+ recommendations.push('Adjust character count to meet requirements');
1253
+ }
1254
+ if (lowerAnalysis.includes('word') && lowerAnalysis.includes('count')) {
1255
+ recommendations.push('Adjust word count to meet requirements');
1256
+ }
1257
+ if (lowerAnalysis.includes('keyword') && lowerAnalysis.includes('missing')) {
1258
+ recommendations.push('Include required keywords');
1259
+ }
1260
+ if (lowerAnalysis.includes('tone') && lowerAnalysis.includes('inappropriate')) {
1261
+ recommendations.push('Adjust tone to meet requirements');
1262
+ }
1263
+ if (lowerAnalysis.includes('structure') && lowerAnalysis.includes('improve')) {
1264
+ recommendations.push('Improve content structure');
1265
+ }
1266
  }
1267
+
1268
+ // FALLBACK PATTERN MATCHING
1269
+ // If no grade found, try alternative patterns
1270
+ if (grade === 'N/A') {
1271
+ const fallbackGradePatterns = [
1272
+ /Grade:\s*(\d+(?:\.\d+)?)\/100/i,
1273
+ /Score:\s*(\d+(?:\.\d+)?)\/100/i,
1274
+ /Rating:\s*(\d+(?:\.\d+)?)\/100/i,
1275
+ /Mark:\s*(\d+(?:\.\d+)?)\/100/i,
1276
+ /Points:\s*(\d+(?:\.\d+)?)\/100/i,
1277
+ /(\d+(?:\.\d+)?)\/100/i,
1278
+ ];
1279
+
1280
+ for (const pattern of fallbackGradePatterns) {
1281
+ const match = sectionBlock.match(pattern);
1282
+ if (match) {
1283
+ grade = `${match[1]}/100`;
1284
+ console.log('Found fallback grade:', grade);
1285
+ break;
1286
+ }
1287
+ }
1288
+ }
1289
+
1290
+ // INFER PASS STATUS FROM GRADE IF NOT DETERMINED
1291
+ if (grade !== 'N/A' && !headerMatch && !complianceMatch) {
1292
+ const gradeNum = parseFloat(grade.split('/')[0]);
1293
+ pass = gradeNum >= 80;
1294
+ console.log('Inferred pass status from grade:', pass);
1295
+ }
1296
+
1297
+ // FINAL PASS STATUS DETERMINATION
1298
+ // If still no pass status, check for pass/fail indicators in text
1299
+ if (!headerMatch && !complianceMatch && grade === 'N/A') {
1300
+ const lowerSection = sectionBlock.toLowerCase();
1301
+ if (lowerSection.includes('pass') && !lowerSection.includes('fail')) {
1302
+ pass = true;
1303
+ } else if (lowerSection.includes('fail')) {
1304
+ pass = false;
1305
+ } else if (lowerSection.includes('✅') && !lowerSection.includes('❌')) {
1306
+ pass = true;
1307
+ } else if (lowerSection.includes('❌')) {
1308
+ pass = false;
1309
+ }
1310
+ }
1311
+
1312
+ // UPDATE ERRORS BASED ON PASS STATUS
1313
+ if (pass && errors[0] === 'No errors reported.') {
1314
+ // Keep as is
1315
+ } else if (!pass && errors[0] === 'No errors reported.') {
1316
+ errors = ['Violations detected'];
1317
+ }
1318
+
1319
+ console.log(`Final ${sectionType} result - Grade: ${grade}, Pass: ${pass}, Errors: ${errors.length}`);
1320
+
1321
+ return {
1322
+ grade,
1323
+ pass,
1324
+ errors,
1325
+ corrected,
1326
+ detailedAssessment: detailedAssessment || undefined,
1327
+ keyStrengths: keyStrengths.length > 0 ? keyStrengths : undefined,
1328
+ recommendations: recommendations.length > 0 ? recommendations : undefined,
1329
+ explanations: explanations || undefined,
1330
+ rawContent: sectionBlock
1331
+ };
1332
  };
1333
 
1334