Spaces:
Sleeping
Sleeping
Update services/difyService.ts
Browse files- services/difyService.ts +220 -114
services/difyService.ts
CHANGED
|
@@ -362,6 +362,193 @@ const extractOverallPass = (text: string): boolean => {
|
|
| 362 |
return false;
|
| 363 |
};
|
| 364 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
/**
|
| 366 |
* Parses the new, structured QA report format.
|
| 367 |
* @param qaText The raw `qa_gaurd` string from the API.
|
|
@@ -375,7 +562,8 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 375 |
meta: { ...defaultSection },
|
| 376 |
h1: { ...defaultSection },
|
| 377 |
copy: { ...defaultSection },
|
| 378 |
-
overall: { grade: 'N/A', pass: false, primaryIssue: 'Parsing failed' }
|
|
|
|
| 379 |
};
|
| 380 |
|
| 381 |
const cleanedQaText = cleanResponseText(qaText);
|
|
@@ -385,7 +573,7 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 385 |
|
| 386 |
// Check if it's the new markdown format (starts with ##) or contains **TITLE** style sections
|
| 387 |
if (cleanedQaText.startsWith('##') || cleanedQaText.includes('**TITLE**') || cleanedQaText.includes('### **') || cleanedQaText.includes('### TITLE')) {
|
| 388 |
-
console.log('Using markdown parser for QA report');
|
| 389 |
console.log('QA text starts with:', cleanedQaText.substring(0, 200));
|
| 390 |
|
| 391 |
// Handle different section header formats
|
|
@@ -413,6 +601,7 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 413 |
|
| 414 |
const parsedData: Partial<DetailedQaReport> = {};
|
| 415 |
let correctedCopyFromSeparateSection = '';
|
|
|
|
| 416 |
|
| 417 |
// Special handling for single-section format like "## GRADE REPORT"
|
| 418 |
if (sections.length === 1 && (sections[0].includes('GRADE REPORT') || sections[0].includes('QUALITY ASSURANCE'))) {
|
|
@@ -429,125 +618,37 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 429 |
header = header.replace(/^#+\s*/, '').replace(/\*\*/g, '').replace(/[:\-–]+$/, '').trim();
|
| 430 |
console.log('Processing header:', header);
|
| 431 |
|
|
|
|
| 432 |
if (header.includes('title')) {
|
| 433 |
-
console.log('Parsing title section');
|
| 434 |
-
parsedData.title =
|
| 435 |
} else if (header.includes('meta')) {
|
| 436 |
-
console.log('Parsing meta section');
|
| 437 |
-
parsedData.meta =
|
| 438 |
} else if (header.includes('h1')) {
|
| 439 |
-
console.log('Parsing h1 section');
|
| 440 |
-
parsedData.h1 =
|
| 441 |
} else if (header.includes('copy') && !header.includes('corrected')) {
|
| 442 |
-
console.log('Parsing copy section');
|
| 443 |
-
parsedData.copy =
|
| 444 |
} else if (header.includes('corrected') && header.includes('copy')) {
|
| 445 |
console.log('Capturing separate CORRECTED COPY section');
|
| 446 |
correctedCopyFromSeparateSection = lines.slice(1).join('\n').trim();
|
| 447 |
} else if (header.includes('overall') || header.includes('assessment') || header.includes('pipeline')) {
|
| 448 |
-
console.log('Parsing overall section');
|
| 449 |
console.log('Overall section text:', sectionBlock.substring(0, 300));
|
| 450 |
|
| 451 |
-
//
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
/(?:•|-|\*)\s*\*\*(?:Final|Total|Overall)?\s*Grade\*\*:?:\s*(.*)/i, // Generic grade
|
| 463 |
-
/(?:•|-|\*)\s*(?:Final|Total|Overall)?\s*Grade:?:\s*(.*)/i, // No bold
|
| 464 |
-
/(?:Final|Total|Overall)\s*Grade:?:\s*(\d+\/\d+|\d+)/im, // Anywhere in text
|
| 465 |
-
/(\d+\/\d+)\s*(?:final|total|overall)?/im // Number first
|
| 466 |
-
];
|
| 467 |
-
|
| 468 |
-
for (const pattern of overallGradePatterns) {
|
| 469 |
-
gradeMatch = sectionBlock.match(pattern);
|
| 470 |
-
if (gradeMatch) {
|
| 471 |
-
grade = gradeMatch[1].trim();
|
| 472 |
-
break;
|
| 473 |
-
}
|
| 474 |
-
}
|
| 475 |
-
console.log('Overall grade match:', gradeMatch, 'Final grade:', grade);
|
| 476 |
-
|
| 477 |
-
// ROBUST OVERALL PASS EXTRACTION - handles multiple formats
|
| 478 |
-
let pass = false;
|
| 479 |
-
let passMatch = null;
|
| 480 |
-
|
| 481 |
-
const overallPassPatterns = [
|
| 482 |
-
// Exact format variations found in logs
|
| 483 |
-
/-\s*\*\*Overall Pass:\*\*\s*(.*)/i, // - **Overall Pass:** true
|
| 484 |
-
/•\s*\*\*Overall Pass:\*\*\s*(.*)/i, // • **Overall Pass:** true
|
| 485 |
-
/\*\s*\*\*Overall Pass:\*\*\s*(.*)/i, // * **Overall Pass:** true
|
| 486 |
-
/•\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // • **All Sections Pass:** true
|
| 487 |
-
/-\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // - **All Sections Pass:** true
|
| 488 |
-
/\*\s*\*\*All Sections Pass:\*\*\s*(.*)/i, // * **All Sections Pass:** true
|
| 489 |
-
/•\s*\*\*Final Pass:\*\*\s*(.*)/i, // • **Final Pass:** true
|
| 490 |
-
/-\s*\*\*Final Pass:\*\*\s*(.*)/i, // - **Final Pass:** true
|
| 491 |
-
/\*\s*\*\*Final Pass:\*\*\s*(.*)/i, // * **Final Pass:** true
|
| 492 |
-
|
| 493 |
-
// Generic patterns with flexible formatting
|
| 494 |
-
/(?:•|-|\*)\s*\*\*(?:Overall\s+|All\s+Sections\s+|Final\s+)?Pass\*\*:?:\s*(.*)/i,
|
| 495 |
-
/(?:•|-|\*)\s*(?:Overall\s+|All\s+Sections\s+|Final\s+)?Pass:?:\s*(.*)/i,
|
| 496 |
-
|
| 497 |
-
// Anywhere in text patterns
|
| 498 |
-
/Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
|
| 499 |
-
/Overall\s*Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
|
| 500 |
-
/All\s*Sections\s*Pass:?:\s*(true|false|✅|❌|TRUE|FALSE)/im,
|
| 501 |
-
/(true|false|✅|❌|TRUE|FALSE)\s*(?:overall|pass)/im,
|
| 502 |
-
|
| 503 |
-
// Handle capitalized boolean values
|
| 504 |
-
/Pass:?:\s*(True|False|TRUE|FALSE)/im,
|
| 505 |
-
/Overall\s*Pass:?:\s*(True|False|TRUE|FALSE)/im
|
| 506 |
-
];
|
| 507 |
-
|
| 508 |
-
for (const pattern of overallPassPatterns) {
|
| 509 |
-
passMatch = sectionBlock.match(pattern);
|
| 510 |
-
if (passMatch) {
|
| 511 |
-
const passValue = passMatch[1].toLowerCase().trim();
|
| 512 |
-
pass = passValue.includes('true') ||
|
| 513 |
-
passValue.includes('✅') ||
|
| 514 |
-
passValue === 'yes' ||
|
| 515 |
-
passValue === 'passed' ||
|
| 516 |
-
passValue === 'pass';
|
| 517 |
-
break;
|
| 518 |
-
}
|
| 519 |
-
}
|
| 520 |
-
console.log('Overall pass match:', passMatch, 'Final pass:', pass);
|
| 521 |
-
|
| 522 |
-
// Look for various primary issue formats
|
| 523 |
-
const explanationMatch = sectionBlock.match(/\*\*Overall Pass\*\*:\s*[^()]*\(([^)]+)\)/);
|
| 524 |
-
const statusMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Pipeline\s+)?Status\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
|
| 525 |
-
const primaryIssueMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Primary\s+)?Issue\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/) ||
|
| 526 |
-
sectionBlock.match(/(?:•|-|\*)\s*(?:Primary\s+)?Issue:?:\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
|
| 527 |
-
const errorsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*Total\s+Errors?\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/);
|
| 528 |
-
const totalSectionsMatch = sectionBlock.match(/Total\s*Sections\s*Passing:?:\s*([^\n]+)/i);
|
| 529 |
-
|
| 530 |
-
let primaryIssue = 'All sections passed successfully.';
|
| 531 |
-
if (explanationMatch) {
|
| 532 |
-
primaryIssue = explanationMatch[1].trim();
|
| 533 |
-
} else if (statusMatch) {
|
| 534 |
-
primaryIssue = statusMatch[1].trim();
|
| 535 |
-
} else if (primaryIssueMatch) {
|
| 536 |
-
primaryIssue = primaryIssueMatch[1].trim();
|
| 537 |
-
} else if (errorsMatch) {
|
| 538 |
-
const errorText = errorsMatch[1].trim();
|
| 539 |
-
if (errorText !== '[]' && errorText !== '') {
|
| 540 |
-
primaryIssue = `Errors found: ${errorText}`;
|
| 541 |
-
}
|
| 542 |
-
}
|
| 543 |
-
if (totalSectionsMatch) {
|
| 544 |
-
primaryIssue = `${primaryIssue} | Total Sections Passing: ${totalSectionsMatch[1].trim()}`;
|
| 545 |
-
}
|
| 546 |
-
|
| 547 |
-
console.log('Primary issue extraction - explanation:', explanationMatch, 'status:', statusMatch, 'issue:', primaryIssueMatch, 'Final issue:', primaryIssue);
|
| 548 |
-
|
| 549 |
-
console.log('Setting overall data - grade:', grade, 'pass:', pass, 'primaryIssue:', primaryIssue);
|
| 550 |
-
parsedData.overall = { grade, pass, primaryIssue };
|
| 551 |
}
|
| 552 |
});
|
| 553 |
|
|
@@ -556,7 +657,9 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 556 |
meta: parsedData.meta || { ...defaultSection, errors: ['Meta section not found'] },
|
| 557 |
h1: parsedData.h1 || { ...defaultSection, errors: ['H1 section not found'] },
|
| 558 |
copy: parsedData.copy || { ...defaultSection, errors: ['Copy section not found'] },
|
| 559 |
-
overall: parsedData.overall || { grade: 'N/A', pass: false, primaryIssue: 'Overall section not found' }
|
|
|
|
|
|
|
| 560 |
};
|
| 561 |
|
| 562 |
// If we saw a separate CORRECTED COPY section, populate copy.corrected with it when useful
|
|
@@ -592,7 +695,8 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 592 |
parsedData.overall = {
|
| 593 |
grade,
|
| 594 |
pass,
|
| 595 |
-
primaryIssue: pass ? 'All sections passed successfully.' : 'Overall assessment failed.'
|
|
|
|
| 596 |
};
|
| 597 |
|
| 598 |
console.log('Found inline overall results - grade:', grade, 'pass:', pass);
|
|
@@ -621,7 +725,8 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 621 |
parsedData.overall = {
|
| 622 |
grade: averageGrade,
|
| 623 |
pass: allSectionsPassed,
|
| 624 |
-
primaryIssue: allSectionsPassed ? 'All sections passed successfully.' : 'One or more sections failed.'
|
|
|
|
| 625 |
};
|
| 626 |
|
| 627 |
console.log('Calculated overall from individual sections - pass:', allSectionsPassed, 'grade:', averageGrade);
|
|
@@ -633,6 +738,7 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
| 633 |
|
| 634 |
console.log('Final parsed QA data:', finalReport.overall);
|
| 635 |
console.log('Setting overallPass:', finalReport.overall.pass, 'overallGrade:', finalReport.overall.grade);
|
|
|
|
| 636 |
|
| 637 |
return {
|
| 638 |
detailedQaReport: finalReport,
|
|
|
|
| 362 |
return false;
|
| 363 |
};
|
| 364 |
|
| 365 |
+
/**
|
| 366 |
+
* Enhanced section parsing that captures ALL QA Guard content
|
| 367 |
+
*/
|
| 368 |
+
const parseEnhancedSection = (sectionBlock: string): QaSectionResult => {
|
| 369 |
+
const baseSection = parseSection(sectionBlock);
|
| 370 |
+
|
| 371 |
+
// Extract detailed assessment content
|
| 372 |
+
const detailedAssessmentMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Detailed\s+)?Assessment\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
|
| 373 |
+
const explanationsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Explanation|Reasoning)\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
|
| 374 |
+
|
| 375 |
+
// Extract key strengths
|
| 376 |
+
const keyStrengthsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*Key\s+Strengths\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
|
| 377 |
+
const strengthsList = keyStrengthsMatch ?
|
| 378 |
+
keyStrengthsMatch[1].split('\n')
|
| 379 |
+
.map(line => line.replace(/^[-•*]\s*/, '').trim())
|
| 380 |
+
.filter(line => line.length > 0) : undefined;
|
| 381 |
+
|
| 382 |
+
// Extract recommendations
|
| 383 |
+
const recommendationsMatch = sectionBlock.match(/(?:•|-|\*)\s*\*\*(?:Recommendations?|Suggestions?)\*\*?:?\s*([\s\S]*?)(?=\n(?:•|-|\*)\s*\*\*|$)/i);
|
| 384 |
+
const recommendationsList = recommendationsMatch ?
|
| 385 |
+
recommendationsMatch[1].split('\n')
|
| 386 |
+
.map(line => line.replace(/^[-•*]\s*/, '').trim())
|
| 387 |
+
.filter(line => line.length > 0) : undefined;
|
| 388 |
+
|
| 389 |
+
return {
|
| 390 |
+
...baseSection,
|
| 391 |
+
detailedAssessment: detailedAssessmentMatch ? detailedAssessmentMatch[1].trim() : undefined,
|
| 392 |
+
explanations: explanationsMatch ? explanationsMatch[1].trim() : undefined,
|
| 393 |
+
keyStrengths: strengthsList,
|
| 394 |
+
recommendations: recommendationsList,
|
| 395 |
+
rawContent: sectionBlock
|
| 396 |
+
};
|
| 397 |
+
};
|
| 398 |
+
|
| 399 |
+
/**
|
| 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 |
+
};
|
| 532 |
+
|
| 533 |
+
/**
|
| 534 |
+
* Determine the type of an additional section based on its content
|
| 535 |
+
*/
|
| 536 |
+
const determineSectionType = (sectionBlock: string): 'assessment' | 'strengths' | 'recommendations' | 'explanations' | 'other' => {
|
| 537 |
+
const lowerContent = sectionBlock.toLowerCase();
|
| 538 |
+
|
| 539 |
+
if (lowerContent.includes('strength') || lowerContent.includes('positive') || lowerContent.includes('excellent')) {
|
| 540 |
+
return 'strengths';
|
| 541 |
+
} else if (lowerContent.includes('recommend') || lowerContent.includes('suggest') || lowerContent.includes('improve')) {
|
| 542 |
+
return 'recommendations';
|
| 543 |
+
} else if (lowerContent.includes('explain') || lowerContent.includes('reason') || lowerContent.includes('why')) {
|
| 544 |
+
return 'explanations';
|
| 545 |
+
} else if (lowerContent.includes('assess') || lowerContent.includes('evaluate') || lowerContent.includes('analysis')) {
|
| 546 |
+
return 'assessment';
|
| 547 |
+
} else {
|
| 548 |
+
return 'other';
|
| 549 |
+
}
|
| 550 |
+
};
|
| 551 |
+
|
| 552 |
/**
|
| 553 |
* Parses the new, structured QA report format.
|
| 554 |
* @param qaText The raw `qa_gaurd` string from the API.
|
|
|
|
| 562 |
meta: { ...defaultSection },
|
| 563 |
h1: { ...defaultSection },
|
| 564 |
copy: { ...defaultSection },
|
| 565 |
+
overall: { grade: 'N/A', pass: false, primaryIssue: 'Parsing failed' },
|
| 566 |
+
completeRawReport: qaText // Always preserve the complete raw report
|
| 567 |
};
|
| 568 |
|
| 569 |
const cleanedQaText = cleanResponseText(qaText);
|
|
|
|
| 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
|
|
|
|
| 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'))) {
|
|
|
|
| 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 |
|
|
|
|
| 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
|
|
|
|
| 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);
|
|
|
|
| 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);
|
|
|
|
| 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,
|