Organized the application into separate frontend and backend directories for cleaner deployment and better separation of concerns. Frontend Directory (frontend/): - index.html: Single-page web interface (renamed from web_ui.html) - README.md: Frontend deployment guide - Total size: ~113 KB (self-contained) - Smart base path detection (works at / or /ai_qc/) - No configuration changes required Backend Directory (backend/): - All Python files (api_server.py, llm_config.py, etc.) - visual_qc_apps/: 33 QC check modules - profiles/: 6 QC profile configurations - brand_guidelines/: Reference asset storage - config/: Environment configurations - scripts/: Deployment automation - uploads/, output/: Data directories - requirements.txt, ai_qc.service, apache_config.conf - Complete documentation New Documentation: - FOLDER_STRUCTURE.md: Comprehensive guide to new structure - frontend/README.md: Frontend deployment instructions - backend/BACKEND_README.md: Backend deployment guide Deployment Mapping: - frontend/ → /var/www/html/ai_qc/ (web root) - backend/ → /opt/ai_qc/ (application directory) Benefits: - Clear separation of concerns - Backend code not in web-accessible directory - Independent frontend/backend updates - Matches server's existing patterns (/opt/veo3, /opt/voice2text) - Industry-standard architecture - Easy to deploy and maintain Original files preserved in root directory for reference. Ready for production deployment following MIGRATION_GUIDE.md. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
170 lines
No EOL
6 KiB
Python
170 lines
No EOL
6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Example: Visual AI QC Headless Operation
|
|
Demonstrates how to use the API without the web interface
|
|
"""
|
|
|
|
import requests
|
|
import time
|
|
import json
|
|
|
|
# Configuration
|
|
API_BASE = "http://localhost:7183"
|
|
IMAGE_PATH = "path/to/your/image.jpg"
|
|
|
|
def headless_analysis_example():
|
|
"""Complete example of headless Visual AI QC analysis"""
|
|
|
|
print("🤖 Visual AI QC - Headless Analysis Example")
|
|
print("=" * 50)
|
|
|
|
# 1. Get available profiles
|
|
print("\n📋 Step 1: Getting available profiles...")
|
|
response = requests.get(f"{API_BASE}/api/profiles")
|
|
if response.status_code == 200:
|
|
profiles = response.json()
|
|
print(f"✅ Found {len(profiles)} profile categories")
|
|
for category, profile_list in profiles.items():
|
|
print(f" {category}: {len(profile_list)} profiles")
|
|
else:
|
|
print(f"❌ Error getting profiles: {response.status_code}")
|
|
return
|
|
|
|
# 2. Get available reference assets
|
|
print("\n📁 Step 2: Getting reference assets...")
|
|
response = requests.get(f"{API_BASE}/api/brand_guidelines")
|
|
if response.status_code == 200:
|
|
guidelines = response.json()
|
|
print(f"✅ Found {len(guidelines.get('files', {}))} reference assets")
|
|
for file_id, file_info in guidelines.get('files', {}).items():
|
|
print(f" {file_info['brand_name']}: {file_info['original_filename']}")
|
|
|
|
# 3. Start analysis with async tracking
|
|
print(f"\n🔍 Step 3: Starting analysis...")
|
|
|
|
with open(IMAGE_PATH, 'rb') as f:
|
|
files = {'file': f}
|
|
data = {
|
|
'profile': 'diageo_key_visual', # Use Diageo profile
|
|
'mode': 'json', # JSON output
|
|
'reference_asset': 'Hellmanns_20250805_113413' # Use reference asset
|
|
}
|
|
|
|
response = requests.post(f"{API_BASE}/api/start_analysis", files=files, data=data)
|
|
|
|
if response.status_code != 200:
|
|
print(f"❌ Error starting analysis: {response.status_code}")
|
|
return
|
|
|
|
result = response.json()
|
|
session_id = result.get('session_id')
|
|
|
|
if not session_id:
|
|
print("❌ No session ID returned")
|
|
return
|
|
|
|
print(f"✅ Analysis started with session ID: {session_id}")
|
|
|
|
# 4. Poll for progress
|
|
print("\n⏳ Step 4: Monitoring progress...")
|
|
while True:
|
|
response = requests.get(f"{API_BASE}/api/progress/{session_id}")
|
|
if response.status_code != 200:
|
|
print(f"❌ Error getting progress: {response.status_code}")
|
|
break
|
|
|
|
progress_data = response.json()
|
|
progress = progress_data.get('progress', {})
|
|
|
|
if progress.get('stage') == 'complete':
|
|
print("✅ Analysis complete!")
|
|
results = progress.get('result', {})
|
|
break
|
|
else:
|
|
current_step = progress.get('current_check_display', 'Processing...')
|
|
percentage = progress.get('percentage', 0)
|
|
completed = progress.get('completed_checks', 0)
|
|
total = progress.get('total_checks', 0)
|
|
|
|
print(f" Progress: {percentage}% - {current_step} ({completed}/{total})")
|
|
time.sleep(2)
|
|
|
|
# 5. Display results
|
|
print("\n📊 Step 5: Analysis Results")
|
|
print("-" * 30)
|
|
|
|
summary = results.get('summary', {})
|
|
print(f"Overall Score: {summary.get('overall_score', 0)}/100")
|
|
print(f"Grade: {summary.get('grade', 'Unknown')}")
|
|
print(f"Checks Performed: {summary.get('checks_count', 0)}")
|
|
|
|
# Show individual check results
|
|
qc_results = results.get('qc_analysis', {}).get('check_results', {})
|
|
print(f"\n🔍 Individual Check Results:")
|
|
for check_name, check_result in qc_results.items():
|
|
if check_result.get('status') == 'completed':
|
|
score = check_result.get('score', 0)
|
|
print(f" {check_name}: {score}/10")
|
|
|
|
# 6. Get output files
|
|
print("\n📄 Step 6: Generated Files")
|
|
response = requests.get(f"{API_BASE}/api/output_files")
|
|
if response.status_code == 200:
|
|
files = response.json()
|
|
print(f"✅ {len(files)} output files available")
|
|
for file_info in files[:3]: # Show latest 3
|
|
print(f" {file_info['filename']} ({file_info['size']})")
|
|
|
|
print("\n🎉 Headless analysis complete!")
|
|
|
|
def simple_analysis_example():
|
|
"""Simplified headless analysis"""
|
|
|
|
print("\n🚀 Simple Headless Analysis")
|
|
print("-" * 30)
|
|
|
|
with open(IMAGE_PATH, 'rb') as f:
|
|
files = {'file': f}
|
|
data = {
|
|
'profile': 'general_key_visual',
|
|
'mode': 'json'
|
|
}
|
|
|
|
# Direct analysis (may take longer but simpler)
|
|
response = requests.post(f"{API_BASE}/api/analyze", files=files, data=data)
|
|
|
|
if response.status_code == 200:
|
|
results = response.json()
|
|
summary = results.get('summary', {})
|
|
print(f"✅ Score: {summary.get('overall_score', 0)}/100")
|
|
print(f" Grade: {summary.get('grade', 'Unknown')}")
|
|
else:
|
|
print(f"❌ Analysis failed: {response.status_code}")
|
|
|
|
def brand_detection_example():
|
|
"""Example of headless brand detection"""
|
|
|
|
print("\n🏷️ Brand Detection Example")
|
|
print("-" * 30)
|
|
|
|
with open(IMAGE_PATH, 'rb') as f:
|
|
files = {'file': f}
|
|
response = requests.post(f"{API_BASE}/api/detect_brand", files=files)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
print(f"✅ Detected Brand: {result.get('brand', 'Unknown')}")
|
|
print(f" Confidence: {result.get('confidence', 0)}")
|
|
else:
|
|
print(f"❌ Brand detection failed: {response.status_code}")
|
|
|
|
if __name__ == "__main__":
|
|
# Run examples (update IMAGE_PATH first!)
|
|
try:
|
|
headless_analysis_example()
|
|
simple_analysis_example()
|
|
brand_detection_example()
|
|
except FileNotFoundError:
|
|
print("❌ Please update IMAGE_PATH with a valid image file path")
|
|
except requests.exceptions.ConnectionError:
|
|
print("❌ Cannot connect to API server. Make sure it's running on localhost:7183") |