ui-regression-tester-intel / agents /agent_5_ai_analyzer.py
riazmo's picture
Upload 22 files
57026c7 verified
"""
Agent 5: AI Analyzer
Uses HuggingFace models for intelligent analysis of UI differences.
Synthesizes findings from visual comparison and element matching.
"""
from typing import Dict, Any, List
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.hf_models import analyze_with_ai, HFModelClient
def agent_5_node(state: Dict[str, Any]) -> Dict[str, Any]:
"""
Agent 5: AI-powered analysis and synthesis.
Uses multiple HF models to:
1. Analyze visual differences in screenshots
2. Generate insights from element comparisons
3. Synthesize findings into actionable report
"""
print("\n" + "="*60)
print("🤖 Agent 5: AI Analyzer - Intelligent Analysis")
print("="*60)
hf_token = state.get("hf_token", "")
logs = state.get("logs", [])
# Get data from previous agents
figma_screenshots = state.get("figma_screenshots", {})
website_screenshots = state.get("website_screenshots", {})
comparison_images = state.get("comparison_images", {})
element_matches = state.get("element_matches", {})
element_differences = state.get("element_differences", [])
similarity_scores = state.get("similarity_scores", {})
overall_score = state.get("overall_score", 0)
# Build elements summary
elements_summary = {
"figma": {},
"website": {},
"matched": 0
}
for viewport, data in element_matches.items():
if isinstance(data, dict) and "statistics" in data:
stats = data["statistics"]
elements_summary["figma"][viewport] = stats.get("total_figma", 0)
elements_summary["website"][viewport] = stats.get("total_website", 0)
elements_summary["matched"] += stats.get("matched", 0)
# Check if AI is available
if not hf_token:
print(" ℹ️ No HF token provided - using rule-based synthesis")
logs.append("ℹ️ AI analysis skipped (no HF token)")
# Fallback synthesis
result = _fallback_analysis(element_differences, similarity_scores, overall_score)
return {
"ai_analysis": result,
"status": "analysis_complete",
"logs": logs
}
print(" 🔑 HF token provided - running AI analysis")
logs.append("🤖 Running AI-powered analysis...")
try:
# Get images for analysis (prefer desktop)
figma_img = figma_screenshots.get("desktop", "")
website_img = website_screenshots.get("desktop", "")
diff_img = comparison_images.get("desktop", "")
scores = {
"overall": overall_score,
"desktop": similarity_scores.get("desktop", 0),
"mobile": similarity_scores.get("mobile", 0)
}
# Run AI analysis
result = analyze_with_ai(
hf_token=hf_token,
figma_image=figma_img,
website_image=website_img,
diff_image=diff_img,
elements_summary=elements_summary,
differences=element_differences,
scores=scores
)
if result.get("ai_enhanced"):
print(" ✅ AI analysis complete")
logs.append("✅ AI analysis successful")
else:
print(" ⚠️ AI analysis fell back to rule-based")
logs.append("⚠️ AI unavailable, used rule-based analysis")
# Add priority issues to logs
for i, issue in enumerate(result.get("priority_issues", [])[:3], 1):
print(f" {i}. {issue[:60]}...")
return {
"ai_analysis": result,
"status": "analysis_complete",
"logs": logs
}
except Exception as e:
print(f" ❌ AI analysis failed: {str(e)}")
logs.append(f"❌ AI error: {str(e)}")
# Fallback
result = _fallback_analysis(element_differences, similarity_scores, overall_score)
return {
"ai_analysis": result,
"status": "analysis_complete",
"logs": logs
}
def _fallback_analysis(
differences: List[Dict],
scores: Dict[str, float],
overall_score: float
) -> Dict[str, Any]:
"""Generate analysis without AI."""
# Count by severity
high = len([d for d in differences if d.get("severity") == "high"])
medium = len([d for d in differences if d.get("severity") == "medium"])
low = len([d for d in differences if d.get("severity") == "low"])
# Count by category
categories = {}
for d in differences:
cat = d.get("category", "unknown")
categories[cat] = categories.get(cat, 0) + 1
# Generate summary
if overall_score >= 90:
summary = f"Excellent match! The website closely follows the Figma design with {overall_score:.0f}% similarity."
elif overall_score >= 75:
summary = f"Good match with {overall_score:.0f}% similarity. {high} high-priority issues need attention."
elif overall_score >= 50:
summary = f"Moderate match at {overall_score:.0f}% similarity. {high + medium} issues detected that require fixes."
else:
summary = f"Significant differences found ({overall_score:.0f}% similarity). Major review needed."
# Priority issues from high severity
priority_issues = []
# Missing elements are highest priority
missing = [d for d in differences if d.get("category") == "missing"]
for m in missing[:2]:
priority_issues.append(f"Missing element: {m.get('element_name', 'Unknown')} not found in website")
# Then high severity issues
high_issues = [d for d in differences if d.get("severity") == "high" and d.get("category") != "missing"]
for h in high_issues[:3]:
priority_issues.append(f"{h.get('category', '').title()}: {h.get('description', 'Check this element')}")
# Fill remaining with medium issues
if len(priority_issues) < 5:
medium_issues = [d for d in differences if d.get("severity") == "medium"]
for m in medium_issues[:5 - len(priority_issues)]:
priority_issues.append(f"{m.get('category', '').title()}: {m.get('description', '')}")
if not priority_issues:
priority_issues = ["No critical issues found. Review minor differences for polish."]
return {
"summary": summary,
"priority_issues": priority_issues,
"ai_enhanced": False,
"statistics": {
"high_severity": high,
"medium_severity": medium,
"low_severity": low,
"by_category": categories
},
"scores": {
"overall": overall_score,
**scores
}
}