diff --git a/backend/app/agents/brand_agent.py b/backend/app/agents/brand_agent.py index 57adc5a..1747cdf 100755 --- a/backend/app/agents/brand_agent.py +++ b/backend/app/agents/brand_agent.py @@ -143,9 +143,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se **Response Format:** - Keep feedback brief and scannable - Use bullet points for each finding -- Each bullet should be one actionable sentence -- Start with the issue, then the recommendation -- Example: "Logo placement incorrect (bottom-left) - move to top-right corner per guidelines" +- Structure each feedback bullet as two clearly labelled parts separated by a line break: + **Issue:** [Clear description of what's wrong] + **Recommendation:** [Actionable fix — what to do and how] +- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**) +- Always place the Recommendation on a new line after the Issue (not on the same line) +- Example: + "• **Issue:** The logo is placed in the bottom-left corner, which doesn't align with brand guidelines. + **Recommendation:** Move the logo to the top-right corner as specified in the brand guidelines." - IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed"). - IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with". - IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users. diff --git a/backend/app/agents/channel_best_practices_agent.py b/backend/app/agents/channel_best_practices_agent.py index 2e293b0..75f4d9e 100644 --- a/backend/app/agents/channel_best_practices_agent.py +++ b/backend/app/agents/channel_best_practices_agent.py @@ -136,9 +136,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se **Response Format:** - Keep feedback brief and scannable - Use bullet points for each finding -- Each bullet should be one actionable sentence -- Start with the issue, then the recommendation -- Example: "CTA placement below fold - move above fold for better visibility" +- Structure each feedback bullet as two clearly labelled parts separated by a line break: + **Issue:** [Clear description of what's wrong] + **Recommendation:** [Actionable fix — what to do and how] +- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**) +- Always place the Recommendation on a new line after the Issue (not on the same line) +- Example: + "• **Issue:** The Call-to-Action (CTA) is placed below the fold, reducing visibility. + **Recommendation:** Move the CTA above the fold so users see it without scrolling." - IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed"). - IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with". - IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users. diff --git a/backend/app/agents/channel_tech_specs_agent.py b/backend/app/agents/channel_tech_specs_agent.py index 8bd5cb7..75d7fd5 100644 --- a/backend/app/agents/channel_tech_specs_agent.py +++ b/backend/app/agents/channel_tech_specs_agent.py @@ -144,9 +144,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se - Then provide your analysis findings below - Keep feedback brief and scannable - Use bullet points for each finding -- Each bullet should be one actionable sentence -- Start with the issue, then the specification requirement -- Example: "Image resolution 72dpi - increase to minimum 150dpi for print quality" +- Structure each feedback bullet as two clearly labelled parts separated by a line break: + **Issue:** [Clear description of what's wrong] + **Recommendation:** [Actionable fix — what to do and how] +- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**) +- Always place the Recommendation on a new line after the Issue (not on the same line) +- Example: + "• **Issue:** The image resolution is 72 DPI (dots per inch), which is below the minimum for this format. + **Recommendation:** Increase the resolution to at least 150 DPI to meet the platform's quality requirements." - IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed"). - IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with". - IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users. diff --git a/backend/app/agents/lead_agent.py b/backend/app/agents/lead_agent.py index 1a7f686..8136be0 100755 --- a/backend/app/agents/lead_agent.py +++ b/backend/app/agents/lead_agent.py @@ -149,8 +149,14 @@ Your summary should: - For 'Analysis Error': "This proof could not be reliably processed." - For 'Requires Manual Legal Review': "This proof requires manual legal review." - Do NOT prefix the opening line with "Verdict:", "Summary:", "Result:", or any other label. -- Follow the opening line with bullet points listing the key actions (max 3-5 bullets) -- Each bullet: one sentence, actionable +- Follow the opening line with bullet points (max 3-5 bullets) +- Structure each bullet as two clearly labelled parts separated by a line break: + **Issue:** [Clear description of what's wrong] + **Recommendation:** [Actionable fix] +- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**) +- Example: + "• **Issue:** The logo is positioned in the top-left corner, which doesn't align with brand guidelines. + **Recommendation:** Move the logo to the bottom-right corner." - Do NOT include page numbers, document names, or source citations. Keep all feedback self-contained and actionable. - For 'Passed': briefly note any amber items in 1-2 bullets - IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed"). diff --git a/backend/app/agents/legal_agent.py b/backend/app/agents/legal_agent.py index e2cf12f..a988cf4 100755 --- a/backend/app/agents/legal_agent.py +++ b/backend/app/agents/legal_agent.py @@ -145,10 +145,15 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se **Response Format:** - Keep feedback brief and scannable - Use bullet points for each finding -- Each bullet should be one actionable sentence +- Structure each feedback bullet as two clearly labelled parts separated by a line break: + **Issue:** [Clear description of what's wrong] + **Recommendation:** [Actionable fix — what to do and how] +- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**) +- Always place the Recommendation on a new line after the Issue (not on the same line) +- Example: + "• **Issue:** The 'save more' claim is a comparative claim without a defined baseline. + **Recommendation:** Clarify what the customer is saving against (e.g. standard interest rates)." - Do NOT include page numbers, document names, or source citations (e.g., no "Page 10", "per the Legal Guidelines"). Feedback must be self-contained and actionable. -- Start with the issue, then the requirement -- Example: "Missing APR disclaimer - add representative APR per FCA requirements" - IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed"). - IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with". - IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users. diff --git a/backend/app/services/gemini_service.py b/backend/app/services/gemini_service.py index fd3575a..f118064 100755 --- a/backend/app/services/gemini_service.py +++ b/backend/app/services/gemini_service.py @@ -70,7 +70,7 @@ class GeminiService: }, "feedback": { "type": "string", - "description": "Brief bullet-point feedback. Each bullet: one actionable sentence. Format: '• Issue: recommendation'. Max 5 bullets." + "description": "Brief bullet-point feedback. Each bullet has two parts: '**Issue:** [problem]' on one line, then '**Recommendation:** [fix]' on the next line. Use • for bullets. Max 5 bullets." }, "issues": { "type": "array", @@ -201,7 +201,7 @@ class GeminiService: }, "feedback": { "type": "string", - "description": "Brief bullet-point feedback. Each bullet: one actionable sentence. Format: '• Issue: recommendation'. Max 5 bullets." + "description": "Brief bullet-point feedback. Each bullet has two parts: '**Issue:** [problem]' on one line, then '**Recommendation:** [fix]' on the next line. Use • for bullets. Max 5 bullets." }, "issues": { "type": "array", diff --git a/frontend/components/FeedbackReport.tsx b/frontend/components/FeedbackReport.tsx index d71ff2f..2e8d02f 100755 --- a/frontend/components/FeedbackReport.tsx +++ b/frontend/components/FeedbackReport.tsx @@ -11,6 +11,31 @@ import { LegalIcon } from './icons/LegalIcon'; * Formats feedback text from Gemini API into properly structured React elements. * Handles HTML tags, bullet characters, and newline-separated text. */ +/** + * Renders inline markdown bold (**text**) as elements. + * Returns an array of React nodes with bold segments wrapped in . + */ +const renderBoldMarkdown = (text: string): React.ReactNode[] => { + const parts: React.ReactNode[] = []; + const regex = /\*\*(.+?)\*\*/g; + let lastIndex = 0; + let match: RegExpExecArray | null; + + while ((match = regex.exec(text)) !== null) { + if (match.index > lastIndex) { + parts.push(text.slice(lastIndex, match.index)); + } + parts.push({match[1]}); + lastIndex = regex.lastIndex; + } + + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)); + } + + return parts; +}; + const formatFeedbackText = (text: string): React.ReactNode => { if (!text) return null; @@ -22,7 +47,7 @@ const formatFeedbackText = (text: string): React.ReactNode => { // Remove opening tags .replace(/]*>/gi, '') .replace(/]*>/gi, '• ') - // Remove any other HTML tags + // Remove any other HTML tags (but preserve ** markdown bold) .replace(/<[^>]+>/g, '') // Normalize whitespace around bullets .replace(/\s*•\s*/g, '\n• ') @@ -34,26 +59,22 @@ const formatFeedbackText = (text: string): React.ReactNode => { const lines = normalizedText.split('\n').filter(line => line.trim()); // Separate intro text from bullet points - const bulletLines: string[] = []; + // Each bullet is an array of lines (to support multi-line Issue/Recommendation) + const bulletGroups: string[][] = []; const introLines: string[] = []; lines.forEach(line => { const trimmed = line.trim(); if (trimmed.startsWith('•')) { - // Remove the bullet character, we'll add it via CSS - bulletLines.push(trimmed.replace(/^•\s*/, '').trim()); + // Start a new bullet group + bulletGroups.push([trimmed.replace(/^•\s*/, '').trim()]); } else if (trimmed) { - // If we haven't started collecting bullets yet, it's intro text - if (bulletLines.length === 0) { + if (bulletGroups.length === 0) { + // Haven't started bullets yet — it's intro text introLines.push(trimmed); } else { - // Text after bullets - treat as continuation of last bullet - const lastBullet = bulletLines.pop(); - if (lastBullet) { - bulletLines.push(`${lastBullet} ${trimmed}`); - } else { - bulletLines.push(trimmed); - } + // Continuation line within the current bullet + bulletGroups[bulletGroups.length - 1].push(trimmed); } } }); @@ -61,12 +82,19 @@ const formatFeedbackText = (text: string): React.ReactNode => { return ( <> {introLines.length > 0 && ( -

{introLines.join(' ')}

+

{renderBoldMarkdown(introLines.join(' '))}

)} - {bulletLines.length > 0 && ( -
    - {bulletLines.map((item, index) => ( -
  • {item}
  • + {bulletGroups.length > 0 && ( +
      + {bulletGroups.map((group, index) => ( +
    • + {group.map((line, lineIdx) => ( + + {lineIdx > 0 &&
      } + {renderBoldMarkdown(line)} +
      + ))} +
    • ))}
    )} diff --git a/frontend/components/PDFReport.tsx b/frontend/components/PDFReport.tsx index 4a2fc83..8859e5f 100755 --- a/frontend/components/PDFReport.tsx +++ b/frontend/components/PDFReport.tsx @@ -11,6 +11,30 @@ interface PDFReportProps { proofs: any[]; } +/** + * Renders inline markdown bold (**text**) as elements for PDF output. + */ +const renderBoldMarkdownForPDF = (text: string): React.ReactNode[] => { + const parts: React.ReactNode[] = []; + const regex = /\*\*(.+?)\*\*/g; + let lastIndex = 0; + let match: RegExpExecArray | null; + + while ((match = regex.exec(text)) !== null) { + if (match.index > lastIndex) { + parts.push(text.slice(lastIndex, match.index)); + } + parts.push({match[1]}); + lastIndex = regex.lastIndex; + } + + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)); + } + + return parts; +}; + const formatFeedbackTextForPDF = (text: string): React.ReactNode => { if (!text) return null; @@ -27,23 +51,20 @@ const formatFeedbackTextForPDF = (text: string): React.ReactNode => { const lines = normalizedText.split('\n').filter(line => line.trim()); - const bulletLines: string[] = []; + // Each bullet is an array of lines (to support multi-line Issue/Recommendation) + const bulletGroups: string[][] = []; const introLines: string[] = []; lines.forEach(line => { const trimmed = line.trim(); if (trimmed.startsWith('•')) { - bulletLines.push(trimmed.replace(/^•\s*/, '').trim()); + bulletGroups.push([trimmed.replace(/^•\s*/, '').trim()]); } else if (trimmed) { - if (bulletLines.length === 0) { + if (bulletGroups.length === 0) { introLines.push(trimmed); } else { - const lastBullet = bulletLines.pop(); - if (lastBullet) { - bulletLines.push(`${lastBullet} ${trimmed}`); - } else { - bulletLines.push(trimmed); - } + // Continuation line within the current bullet + bulletGroups[bulletGroups.length - 1].push(trimmed); } } }); @@ -51,12 +72,19 @@ const formatFeedbackTextForPDF = (text: string): React.ReactNode => { return ( <> {introLines.length > 0 && ( -

    {introLines.join(' ')}

    +

    {renderBoldMarkdownForPDF(introLines.join(' '))}

    )} - {bulletLines.length > 0 && ( + {bulletGroups.length > 0 && (
      - {bulletLines.map((item, index) => ( -
    • {item}
    • + {bulletGroups.map((group, index) => ( +
    • + {group.map((line, lineIdx) => ( + + {lineIdx > 0 &&
      } + {renderBoldMarkdownForPDF(line)} +
      + ))} +
    • ))}
    )}