modcomms/backend/app/agents/channel_tech_specs_agent.py
michael a957cf0276 Pass proof metadata (channel, sub-channel, proof type) to AI agents during analysis
Previously, proof metadata collected during upload was only used for database
persistence. Now it flows through the entire analysis pipeline so agents can
tailor their feedback to the specific channel and format being reviewed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:30:38 -06:00

160 lines
6.3 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,
channel: Optional[str] = None,
sub_channel: Optional[str] = None,
proof_type: Optional[str] = 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
channel: Target channel (e.g. "Social", "Digital")
sub_channel: Target sub-channel (e.g. "Meta", "Google")
proof_type: Proof format type (e.g. "In-feed 1x1", "Banner")
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}
---
**PROOF METADATA**
- Channel: {channel or "Not specified"}
- Sub-Channel: {sub_channel or "Not specified"}
- Proof Type: {proof_type or "Not specified"}
Use this metadata to focus your analysis on the specific technical specifications for this channel and format.
---
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
)