modcomms/backend/app/agents/channel_tech_specs_agent.py
michael 3a5c3bcde3 Implement revision-aware proof analysis pipeline
When a subsequent revision of a proof is uploaded, the analysis now takes
place in context of the previous version's results. The system identifies:
- Resolved issues: fixed in the new revision
- Outstanding issues: still present from previous version
- New issues: introduced in the new revision

Key changes:
- Add resolvedIssues, outstandingIssues, newIssues fields to SubReview
- Add PreviousReviewContext model for passing previous review data
- Update all specialist agents to accept previous_review context
- Extend GeminiService with include_revision_fields parameter
- Add get_latest_version_review() repository method
- Update LeadAgent to synthesize cross-version context in summary
- Fetch previous analysis in WebSocket handler for revisions

First version analysis continues to work exactly as before with revision
fields set to null.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 10:04:16 -06:00

146 lines
5.7 KiB
Python

from typing import List, Optional, Tuple
from app.agents.base_agent import BaseAgent
from app.models.schemas import PreviousReviewContext, SubReview
from app.services.gemini_service import GeminiService
from app.services.reference_docs import ReferenceDocsService
class ChannelTechSpecsAgent(BaseAgent):
"""Channel Tech Specs Agent - analyzes proofs for technical specifications and format requirements using Gemini."""
name = "Channel Tech Specs Agent"
def __init__(self, gemini_service: GeminiService, reference_docs: ReferenceDocsService):
"""
Initialize the Channel Tech Specs Agent.
Args:
gemini_service: Service for making Gemini API calls
reference_docs: Service for loading reference documents
"""
self.gemini = gemini_service
self.reference_docs = reference_docs
def _build_revision_context(self, previous_review: PreviousReviewContext) -> str:
"""Build prompt section for revision-aware analysis."""
issues_list = "\n".join(f" - {issue}" for issue in previous_review.issues) if previous_review.issues else " (No issues)"
return f"""
---
**REVISION CONTEXT**
This is a revision of a previously reviewed proof. The previous version (Version {previous_review.version}) had the following channel tech specs review:
- RAG Status: {previous_review.ragStatus}
- Feedback: {previous_review.feedback}
- Issues identified:
{issues_list}
When analyzing this revision, you MUST:
1. Compare against the previous issues and determine which have been RESOLVED
2. Identify which previous issues are still OUTSTANDING (not fixed)
3. Identify any NEW issues introduced in this revision
Your response MUST include:
- resolvedIssues: Array of issues from the previous version that have been fixed
- outstandingIssues: Array of issues from the previous version that remain unfixed
- newIssues: Array of new issues not present in the previous version
---
"""
async def analyze(
self,
images: List[Tuple[bytes, str]],
previous_review: Optional[PreviousReviewContext] = None,
) -> SubReview:
"""
Analyze the proof for technical specifications compliance.
Args:
images: List of (file_data, mime_type) tuples representing the proof
previous_review: Optional context from previous version for revision-aware analysis
Returns:
SubReview with technical specifications assessment
"""
# Get the channel tech specs specification
tech_specs_context = self.reference_docs.get_channel_tech_specs_spec()
# Build revision context if available
revision_context = ""
if previous_review:
revision_context = self._build_revision_context(previous_review)
prompt = f"""You are a digital channel technical specifications specialist for Barclays Bank. Your role is to analyze marketing proofs for technical compliance with platform specifications, dimensions, file formats, and character limits.
Here are the channel technical specifications to use for your analysis:
{tech_specs_context}
{revision_context}
---
Analyze the uploaded proof for technical specification compliance, checking:
1. **Dimensions & Resolution**:
- Does the asset meet the required dimensions for the target platform?
- Is the resolution appropriate (DPI/PPI requirements)?
- Are aspect ratios correct for the intended placement?
2. **File Format Requirements**:
- Is the file format suitable for the platform?
- Are file size limits being respected?
- Is compression appropriate for quality vs. performance?
3. **Typography Specifications**:
- Are minimum font sizes met for the platform?
- Character counts within platform limits (headlines, body, etc.)?
- Is text readable at the intended display size?
4. **Digital Grid System**:
- Desktop: 12-column grid compliance
- Tablet: 12-column grid compliance
- Mobile: 6-column grid compliance
- 8px baseline grid adherence
5. **Accessibility Requirements**:
- Color contrast meets WCAG requirements?
- Only documented color pairings are used?
- Text is legible at intended display sizes?
6. **Platform-Specific Technical Requirements**:
- Safe zone compliance for interactive elements
- Video/animation format requirements (if applicable)
- Frame rate and duration limits (if applicable)
Provide your analysis as a JSON object. Be specific about any technical issues and reference the relevant specification.
RAG Status Guidelines:
- **Green**: Fully compliant with all technical specifications
- **Amber**: Minor technical adjustments needed but content is deployable
- **Red**: Significant technical issues that will prevent proper display or deployment
If the proof is nonsensical, not a marketing material, or cannot be analyzed, set analysisStatus to 'low_confidence'.
**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 specification requirement
- Example: "Image resolution 72dpi - increase to minimum 150dpi for print quality"
"""
# Determine if revision fields should be included
include_revision_fields = previous_review is not None
# Use single-image or multi-image analysis depending on input
if len(images) == 1:
file_data, file_type = images[0]
return await self.gemini.analyze_with_image(
prompt, file_data, file_type, include_revision_fields=include_revision_fields
)
else:
return await self.gemini.analyze_with_images(
prompt, images, include_revision_fields=include_revision_fields
)