Spaces:
Runtime error
🚀 Complete A/B Test Predictor with Enhanced Dual-AI Analysis
Browse files✨ Features:
- 🤖 Dual-AI powered analysis (Perplexity + Gemini Pro)
- 🎯 359 A/B test pattern detection with rich context
- 📊 Confidence scoring based on training statistics
- 🔍 Enhanced GGG model architecture with real trained weights
- 📝 OCR text extraction and multimodal fusion
🔧 Technical Stack:
- SupervisedSiameseMultimodal with GGG enhancements
- Perplexity Sonar Reasoning Pro for business categorization
- Gemini Pro Vision for visual pattern detection
- Industry + Page Type confidence scoring (avg 160 samples)
- Hugging Face Model Hub integration for large model files
📊 Model Architecture:
- Vision: ViT (google/vit-base-patch16-224-in21k)
- Text: DistilBERT (distilbert-base-uncased)
- Fusion: Gated fusion with directional features
- Categories: 5 categorical features with embeddings
- Enhanced: BatchNorm + Fusion Block + Role Embedding
🎯 Capabilities:
- Smart auto-prediction with zero manual input
- Manual category selection for precise control
- Batch prediction from CSV files
- Comprehensive result analysis with confidence metrics
- Pattern identification from 359 possible A/B test modifications
📁 Files Included:
- app.py: Complete application with Model Hub integration
- metadata.js: Category definitions from training data
- confidence_scores.js: Statistical confidence scores
- patterbs.json: Rich pattern descriptions for AI analysis
- model/: Category mappings for GGG model
- requirements.txt: All dependencies including huggingface_hub
🔑 Setup Required:
- Set PERPLEXITY_API_KEY in Space secrets
- Set GEMINI_API_KEY in Space secrets
- Upload multimodal_cat_mappings_GGG.json to model repo
- Model will auto-download from nitish-spz/ABTestPredictor
Ready for production deployment! 🚀
- README.md +72 -13
- SETUP_MODEL_HUB.md +80 -0
- app.py +1045 -0
- confidence_scores.js +583 -0
- metadata.js +604 -0
- model/multimodal_cat_mappings_GGG.json +60 -0
- packages.txt +2 -0
- patterbs.json +0 -0
- requirements.txt +11 -0
- setup_instructions.md +56 -0
|
@@ -1,13 +1,72 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Multimodal A/B Test Predictor
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
Advanced A/B testing outcome predictor using multimodal AI analysis combining:
|
| 5 |
+
- 🖼️ **Image Analysis**: Visual features from control & variant images
|
| 6 |
+
- 📝 **OCR Text Extraction**: Automatically extracts and analyzes text from images
|
| 7 |
+
- 📊 **Categorical Features**: Business context (industry, page type, etc.)
|
| 8 |
+
- 🎯 **Confidence Scores**: Based on training data statistics and historical accuracy
|
| 9 |
+
|
| 10 |
+
## 🤖 Dual-AI Architecture
|
| 11 |
+
|
| 12 |
+
### **Perplexity Sonar Reasoning Pro** (Business Categorization)
|
| 13 |
+
- Analyzes business context from both images
|
| 14 |
+
- Categorizes: Business Model, Customer Type, Conversion Type, Industry, Page Type
|
| 15 |
+
- Advanced reasoning capabilities for business context understanding
|
| 16 |
+
|
| 17 |
+
### **Gemini Pro Vision** (Pattern Detection)
|
| 18 |
+
- Compares control vs variant images to identify specific A/B test patterns
|
| 19 |
+
- Analyzes against 359 possible A/B testing patterns with rich context
|
| 20 |
+
- Superior visual understanding for precise pattern identification
|
| 21 |
+
|
| 22 |
+
## 🎯 Features
|
| 23 |
+
|
| 24 |
+
### Smart Auto-Prediction
|
| 25 |
+
- Upload control & variant images
|
| 26 |
+
- AI automatically detects all categories and patterns
|
| 27 |
+
- One-click prediction with comprehensive analysis
|
| 28 |
+
|
| 29 |
+
### Enhanced Results
|
| 30 |
+
- **Winner Prediction**: Variant vs Control with probability
|
| 31 |
+
- **Model Confidence**: Accuracy percentage from training data
|
| 32 |
+
- **Training Data Count**: Number of samples model trained on
|
| 33 |
+
- **Historical Win/Loss**: Real A/B test outcome statistics
|
| 34 |
+
- **Detected Pattern**: Specific A/B test modification identified
|
| 35 |
+
|
| 36 |
+
## 🔧 Setup
|
| 37 |
+
|
| 38 |
+
### Required API Keys (Set in Spaces Settings → Variables and secrets)
|
| 39 |
+
- `PERPLEXITY_API_KEY`: For business categorization
|
| 40 |
+
- `GEMINI_API_KEY`: For visual pattern detection
|
| 41 |
+
|
| 42 |
+
### Model Files
|
| 43 |
+
- `model/multimodal_gated_model_2.7_GGG.pth`: Enhanced multimodal model (789MB)
|
| 44 |
+
- `model/multimodal_cat_mappings_GGG.json`: Category mappings
|
| 45 |
+
|
| 46 |
+
## 🚀 Technical Architecture
|
| 47 |
+
|
| 48 |
+
### Model: SupervisedSiameseMultimodal (GGG Enhanced)
|
| 49 |
+
- **Vision**: ViT (Vision Transformer) for image features
|
| 50 |
+
- **Text**: DistilBERT for OCR text processing
|
| 51 |
+
- **Fusion**: Gated fusion with directional features
|
| 52 |
+
- **Categories**: Embedding layers for categorical features
|
| 53 |
+
- **Architecture**: BatchNorm + Fusion Block + Enhanced Prediction Head
|
| 54 |
+
|
| 55 |
+
### Confidence Scoring
|
| 56 |
+
- Based on Industry + Page Type combinations
|
| 57 |
+
- Uses holdout statistics with average 160 samples per combination
|
| 58 |
+
- Much more reliable than full 5-feature combinations
|
| 59 |
+
|
| 60 |
+
## 📊 Performance
|
| 61 |
+
- **Multimodal Analysis**: Images + Text + Categories
|
| 62 |
+
- **Parallel Processing**: Dual-AI calls for optimal speed
|
| 63 |
+
- **High Accuracy**: Enhanced GGG architecture with real training data
|
| 64 |
+
- **Robust Fallbacks**: Graceful degradation if APIs unavailable
|
| 65 |
+
|
| 66 |
+
## 🎯 Use Cases
|
| 67 |
+
- **A/B Test Prediction**: Predict winners before running tests
|
| 68 |
+
- **Pattern Analysis**: Identify what changes were made in variants
|
| 69 |
+
- **Business Context**: Automatic categorization of test context
|
| 70 |
+
- **Confidence Assessment**: Understand prediction reliability
|
| 71 |
+
|
| 72 |
+
Built with ❤️ using Gradio, PyTorch, Transformers, and advanced AI APIs.
|
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🎯 Hugging Face Model Hub Integration Setup
|
| 2 |
+
|
| 3 |
+
## Current Setup
|
| 4 |
+
You've uploaded your model to **Hugging Face Models** (not Spaces). This is actually the BEST approach for large models!
|
| 5 |
+
|
| 6 |
+
## 📁 Your Model Repository Structure
|
| 7 |
+
Your model repo `nitish-spz/ABTestPredictor` should contain:
|
| 8 |
+
```
|
| 9 |
+
nitish-spz/ABTestPredictor/
|
| 10 |
+
├── multimodal_gated_model_2.7_GGG.pth (789MB) ✅ Already uploaded
|
| 11 |
+
├── multimodal_cat_mappings_GGG.json (1.5KB) - Need to upload
|
| 12 |
+
└── README.md (optional)
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
## 🚀 Setup Steps
|
| 16 |
+
|
| 17 |
+
### Step 1: Upload Missing Files to Model Repository
|
| 18 |
+
Go to your model repo: `https://huggingface.co/nitish-spz/ABTestPredictor`
|
| 19 |
+
|
| 20 |
+
**Upload these files:**
|
| 21 |
+
1. **`multimodal_cat_mappings_GGG.json`** (from `ABTestPredictor_NEW/model/` folder)
|
| 22 |
+
2. **`README.md`** (optional - documents your model)
|
| 23 |
+
|
| 24 |
+
### Step 2: Create New Hugging Face Space
|
| 25 |
+
1. Go to https://huggingface.co/new-space
|
| 26 |
+
2. Create a new Space (e.g., `nitish-spz/ABTestPredictorApp`)
|
| 27 |
+
3. Choose **Gradio** as the SDK
|
| 28 |
+
|
| 29 |
+
### Step 3: Upload App Files to Space
|
| 30 |
+
Upload these files from `ABTestPredictor_NEW/` to your new Space:
|
| 31 |
+
```
|
| 32 |
+
├── app.py (✅ Updated with Model Hub integration)
|
| 33 |
+
├── requirements.txt (✅ Includes huggingface_hub)
|
| 34 |
+
├── packages.txt
|
| 35 |
+
├── metadata.js
|
| 36 |
+
├── confidence_scores.js
|
| 37 |
+
├── patterbs.json
|
| 38 |
+
└── README.md
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
### Step 4: Set API Keys in Space Settings
|
| 42 |
+
In your Space Settings → Variables and secrets:
|
| 43 |
+
- **`PERPLEXITY_API_KEY`**: For business categorization
|
| 44 |
+
- **`GEMINI_API_KEY`**: For pattern detection
|
| 45 |
+
|
| 46 |
+
## 🔧 How It Works
|
| 47 |
+
|
| 48 |
+
### Model Loading Process:
|
| 49 |
+
1. **App starts** → Checks for local model file
|
| 50 |
+
2. **Downloads from Hub** → `hf_hub_download("nitish-spz/ABTestPredictor", "multimodal_gated_model_2.7_GGG.pth")`
|
| 51 |
+
3. **Loads weights** → Into enhanced GGG architecture
|
| 52 |
+
4. **Ready for predictions** → With real trained weights
|
| 53 |
+
|
| 54 |
+
### Benefits:
|
| 55 |
+
- ✅ **Large Model Support**: No 1GB Space limit issues
|
| 56 |
+
- ✅ **Version Control**: Model Hub handles large file versioning
|
| 57 |
+
- ✅ **Automatic Download**: App downloads model on first run
|
| 58 |
+
- ✅ **Caching**: Model cached locally after first download
|
| 59 |
+
|
| 60 |
+
## 🎯 Expected Results
|
| 61 |
+
|
| 62 |
+
After setup, your app will:
|
| 63 |
+
1. **Download your 789MB model** automatically from Model Hub
|
| 64 |
+
2. **Load real trained weights** (not dummy initialization)
|
| 65 |
+
3. **Provide accurate predictions** with enhanced GGG architecture
|
| 66 |
+
4. **Run dual-AI analysis** with Perplexity + Gemini Pro
|
| 67 |
+
|
| 68 |
+
## ⚡ Quick Setup Commands
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
# If you want to use git for the Space:
|
| 72 |
+
git clone https://huggingface.co/spaces/nitish-spz/YourNewSpaceName
|
| 73 |
+
cd YourNewSpaceName
|
| 74 |
+
cp /path/to/ABTestPredictor_NEW/* .
|
| 75 |
+
git add .
|
| 76 |
+
git commit -m "Complete A/B test predictor with Model Hub integration"
|
| 77 |
+
git push
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
Your enhanced A/B test predictor is ready for deployment with Model Hub integration! 🚀
|
|
@@ -0,0 +1,1045 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
from PIL import Image
|
| 7 |
+
import numpy as np
|
| 8 |
+
import pandas as pd
|
| 9 |
+
from transformers import AutoProcessor, ViTModel, AutoTokenizer, AutoModel
|
| 10 |
+
from huggingface_hub import hf_hub_download
|
| 11 |
+
import gradio as gr
|
| 12 |
+
import pytesseract # For OCR
|
| 13 |
+
import spaces
|
| 14 |
+
import random
|
| 15 |
+
import time
|
| 16 |
+
import subprocess
|
| 17 |
+
import requests
|
| 18 |
+
import base64
|
| 19 |
+
import re
|
| 20 |
+
from io import BytesIO
|
| 21 |
+
|
| 22 |
+
# --- 1. Configuration (Mirrored from your scripts) ---
|
| 23 |
+
# This ensures consistency with the model's training environment.
|
| 24 |
+
MODEL_DIR = "model"
|
| 25 |
+
MODEL_SAVE_PATH = os.path.join(MODEL_DIR, "multimodal_gated_model_2.7_GGG.pth")
|
| 26 |
+
CAT_MAPPINGS_SAVE_PATH = os.path.join(MODEL_DIR, "multimodal_cat_mappings_GGG.json")
|
| 27 |
+
|
| 28 |
+
# Perplexity API Configuration (for categorization)
|
| 29 |
+
PERPLEXITY_API_KEY = os.getenv("PERPLEXITY_API_KEY") # Set this in Hugging Face Spaces secrets
|
| 30 |
+
PERPLEXITY_API_URL = "https://api.perplexity.ai/chat/completions"
|
| 31 |
+
|
| 32 |
+
# Gemini Pro API Configuration (for pattern detection)
|
| 33 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") # Set this in Hugging Face Spaces secrets
|
| 34 |
+
GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent"
|
| 35 |
+
|
| 36 |
+
# Hugging Face Model Hub Configuration
|
| 37 |
+
HF_MODEL_REPO = "nitish-spz/ABTestPredictor" # Your model repository
|
| 38 |
+
HF_MODEL_FILENAME = "multimodal_gated_model_2.7_GGG.pth"
|
| 39 |
+
HF_MAPPINGS_FILENAME = "multimodal_cat_mappings_GGG.json"
|
| 40 |
+
|
| 41 |
+
VISION_MODEL_NAME = "google/vit-base-patch16-224-in21k"
|
| 42 |
+
TEXT_MODEL_NAME = "distilbert-base-uncased"
|
| 43 |
+
MAX_TEXT_LENGTH = 512
|
| 44 |
+
|
| 45 |
+
# Columns from testing script
|
| 46 |
+
CONTROL_IMAGE_URL_COLUMN = "controlImage"
|
| 47 |
+
VARIANT_IMAGE_URL_COLUMN = "variantImage"
|
| 48 |
+
|
| 49 |
+
CATEGORICAL_FEATURES = [
|
| 50 |
+
"Business Model", "Customer Type", "grouped_conversion_type",
|
| 51 |
+
"grouped_industry", "grouped_page_type"
|
| 52 |
+
]
|
| 53 |
+
CATEGORICAL_EMBEDDING_DIMS = {
|
| 54 |
+
"Business Model": 10, "Customer Type": 10, "grouped_conversion_type": 25,
|
| 55 |
+
"grouped_industry": 50, "grouped_page_type": 25
|
| 56 |
+
}
|
| 57 |
+
GATED_FUSION_DIM = 64
|
| 58 |
+
|
| 59 |
+
# --- 2. Model Architecture (Exact Replica from your training script) ---
|
| 60 |
+
# This class must be defined to load the saved model weights correctly.
|
| 61 |
+
class SupervisedSiameseMultimodal(nn.Module):
|
| 62 |
+
"""
|
| 63 |
+
Updated model architecture matching the new GGG version.
|
| 64 |
+
Includes fusion block, BatchNorm, and enhanced directional features.
|
| 65 |
+
"""
|
| 66 |
+
def __init__(self, vision_model_name, text_model_name, cat_mappings, cat_embedding_dims):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.vision_model = ViTModel.from_pretrained(vision_model_name)
|
| 69 |
+
self.text_model = AutoModel.from_pretrained(text_model_name)
|
| 70 |
+
|
| 71 |
+
vision_dim = self.vision_model.config.hidden_size
|
| 72 |
+
text_dim = self.text_model.config.hidden_size
|
| 73 |
+
|
| 74 |
+
self.embedding_layers = nn.ModuleList()
|
| 75 |
+
total_cat_emb_dim = 0
|
| 76 |
+
for feature in CATEGORICAL_FEATURES:
|
| 77 |
+
# Safely handle cases where a feature might not be in mappings
|
| 78 |
+
if feature in cat_mappings:
|
| 79 |
+
num_cats = cat_mappings[feature]['num_categories']
|
| 80 |
+
emb_dim = cat_embedding_dims[feature]
|
| 81 |
+
self.embedding_layers.append(nn.Embedding(num_cats, emb_dim))
|
| 82 |
+
total_cat_emb_dim += emb_dim
|
| 83 |
+
|
| 84 |
+
self.gate_controller = nn.Sequential(
|
| 85 |
+
nn.Linear(total_cat_emb_dim, GATED_FUSION_DIM),
|
| 86 |
+
nn.ReLU(),
|
| 87 |
+
nn.Linear(GATED_FUSION_DIM, 2)
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
# Updated in_dim calculation to match new architecture
|
| 91 |
+
in_dim = (vision_dim * 4) + (text_dim * 4) + total_cat_emb_dim + 2
|
| 92 |
+
|
| 93 |
+
# Add the fusion block
|
| 94 |
+
self.fusion_block = nn.Sequential(
|
| 95 |
+
nn.Linear(in_dim, in_dim),
|
| 96 |
+
nn.ReLU(),
|
| 97 |
+
nn.Dropout(0.2)
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# Updated prediction head with BatchNorm
|
| 101 |
+
self.prediction_head = nn.Sequential(
|
| 102 |
+
nn.BatchNorm1d(in_dim),
|
| 103 |
+
nn.Linear(in_dim, vision_dim),
|
| 104 |
+
nn.GELU(),
|
| 105 |
+
nn.LayerNorm(vision_dim),
|
| 106 |
+
nn.Dropout(0.2),
|
| 107 |
+
nn.Linear(vision_dim, vision_dim // 2),
|
| 108 |
+
nn.GELU(),
|
| 109 |
+
nn.LayerNorm(vision_dim // 2),
|
| 110 |
+
nn.Dropout(0.1),
|
| 111 |
+
nn.Linear(vision_dim // 2, 1)
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
def forward(self, c_pix, v_pix, c_tok, c_attn, v_tok, v_attn, cat_feats):
|
| 115 |
+
# Enhanced forward pass with directional features
|
| 116 |
+
emb_c_vision = self.vision_model(pixel_values=c_pix).pooler_output
|
| 117 |
+
emb_v_vision = self.vision_model(pixel_values=v_pix).pooler_output
|
| 118 |
+
direction_feat_vision = torch.cat([emb_c_vision - emb_v_vision, emb_v_vision - emb_c_vision], dim=1)
|
| 119 |
+
|
| 120 |
+
c_text_out = self.text_model(input_ids=c_tok, attention_mask=c_attn).last_hidden_state
|
| 121 |
+
v_text_out = self.text_model(input_ids=v_tok, attention_mask=v_attn).last_hidden_state
|
| 122 |
+
emb_c_text = c_text_out.mean(dim=1)
|
| 123 |
+
emb_v_text = v_text_out.mean(dim=1)
|
| 124 |
+
direction_feat_text = torch.cat([emb_c_text - emb_v_text, emb_v_text - emb_c_text], dim=1)
|
| 125 |
+
|
| 126 |
+
cat_embeddings = [layer(cat_feats[:, i]) for i, layer in enumerate(self.embedding_layers)]
|
| 127 |
+
final_cat_embedding = torch.cat(cat_embeddings, dim=1)
|
| 128 |
+
|
| 129 |
+
gates = F.softmax(self.gate_controller(final_cat_embedding), dim=-1)
|
| 130 |
+
vision_gate = gates[:, 0].unsqueeze(1)
|
| 131 |
+
text_gate = gates[:, 1].unsqueeze(1)
|
| 132 |
+
|
| 133 |
+
weighted_vision = direction_feat_vision * vision_gate
|
| 134 |
+
weighted_text = direction_feat_text * text_gate
|
| 135 |
+
|
| 136 |
+
batch_size = c_pix.shape[0]
|
| 137 |
+
role_embedding = torch.tensor([[1, 0]] * batch_size, dtype=torch.float32, device=c_pix.device)
|
| 138 |
+
|
| 139 |
+
final_vector = torch.cat([
|
| 140 |
+
emb_c_vision, emb_v_vision,
|
| 141 |
+
emb_c_text, emb_v_text,
|
| 142 |
+
weighted_vision, weighted_text,
|
| 143 |
+
final_cat_embedding,
|
| 144 |
+
role_embedding
|
| 145 |
+
], dim=1)
|
| 146 |
+
|
| 147 |
+
# Pass through the fusion block before the final prediction head
|
| 148 |
+
fused_vector = self.fusion_block(final_vector)
|
| 149 |
+
|
| 150 |
+
return self.prediction_head(fused_vector).squeeze(-1)
|
| 151 |
+
|
| 152 |
+
# --- 3. Loading Models and Processors (Done once on startup) ---
|
| 153 |
+
# Optimized for L4 GPU setup
|
| 154 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 155 |
+
print(f"🚀 Using device: {device}")
|
| 156 |
+
if torch.cuda.is_available():
|
| 157 |
+
print(f"🔥 GPU: {torch.cuda.get_device_name(0)}")
|
| 158 |
+
print(f"💾 GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB")
|
| 159 |
+
# AGGRESSIVE optimizations for 4x L4 GPU
|
| 160 |
+
torch.backends.cudnn.benchmark = True
|
| 161 |
+
torch.backends.cudnn.enabled = True
|
| 162 |
+
torch.backends.cudnn.deterministic = False # Allow non-deterministic for speed
|
| 163 |
+
# Aggressive memory management
|
| 164 |
+
torch.cuda.empty_cache()
|
| 165 |
+
# Enable tensor core usage for maximum performance
|
| 166 |
+
torch.backends.cuda.matmul.allow_tf32 = True
|
| 167 |
+
torch.backends.cudnn.allow_tf32 = True
|
| 168 |
+
|
| 169 |
+
# Create dummy files if they don't exist for the app to run
|
| 170 |
+
if not os.path.exists(MODEL_DIR):
|
| 171 |
+
os.makedirs(MODEL_DIR)
|
| 172 |
+
|
| 173 |
+
if not os.path.exists(CAT_MAPPINGS_SAVE_PATH):
|
| 174 |
+
print(f"⚠️ GGG Category mappings not found. Loading from metadata.js...")
|
| 175 |
+
# Import the real metadata from metadata.js
|
| 176 |
+
import subprocess
|
| 177 |
+
import sys
|
| 178 |
+
|
| 179 |
+
try:
|
| 180 |
+
# Use Node.js to extract the categoryMappings from metadata.js
|
| 181 |
+
result = subprocess.run([
|
| 182 |
+
'node', '-e',
|
| 183 |
+
'const meta = require("./metadata.js"); console.log(JSON.stringify(meta.categoryMappings));'
|
| 184 |
+
], capture_output=True, text=True, cwd='.')
|
| 185 |
+
|
| 186 |
+
if result.returncode == 0:
|
| 187 |
+
category_mappings_from_js = json.loads(result.stdout.strip())
|
| 188 |
+
print(f"✅ Successfully loaded category mappings from metadata.js for GGG model")
|
| 189 |
+
with open(CAT_MAPPINGS_SAVE_PATH, 'w') as f:
|
| 190 |
+
json.dump(category_mappings_from_js, f, indent=2)
|
| 191 |
+
else:
|
| 192 |
+
print(f"⚠️ Failed to load from metadata.js: {result.stderr}")
|
| 193 |
+
print("Creating GGG-compatible dummy mappings as fallback...")
|
| 194 |
+
dummy_mappings = {
|
| 195 |
+
"Business Model": {"num_categories": 4, "categories": ["E-Commerce", "Lead Generation", "Other*", "SaaS"]},
|
| 196 |
+
"Customer Type": {"num_categories": 4, "categories": ["B2B", "B2C", "Both", "Other*"]},
|
| 197 |
+
"grouped_conversion_type": {"num_categories": 6, "categories": ["Direct Purchase", "High-Intent Lead Gen", "Info/Content Lead Gen", "Location Search", "Non-Profit/Community", "Other Conversion"]},
|
| 198 |
+
"grouped_industry": {"num_categories": 14, "categories": ["Automotive & Transportation", "B2B Services", "B2B Software & Tech", "Consumer Services", "Consumer Software & Apps", "Education", "Finance, Insurance & Real Estate", "Food, Hospitality & Travel", "Health & Wellness", "Industrial & Manufacturing", "Media & Entertainment", "Non-Profit & Government", "Other", "Retail & E-commerce"]},
|
| 199 |
+
"grouped_page_type": {"num_categories": 5, "categories": ["Awareness & Discovery", "Consideration & Evaluation", "Conversion", "Internal & Navigation", "Post-Conversion & Other"]}
|
| 200 |
+
}
|
| 201 |
+
with open(CAT_MAPPINGS_SAVE_PATH, 'w') as f:
|
| 202 |
+
json.dump(dummy_mappings, f, indent=2)
|
| 203 |
+
except Exception as e:
|
| 204 |
+
print(f"⚠️ Error loading metadata.js: {e}")
|
| 205 |
+
print("Creating GGG-compatible dummy mappings as fallback...")
|
| 206 |
+
dummy_mappings = {
|
| 207 |
+
"Business Model": {"num_categories": 4, "categories": ["E-Commerce", "Lead Generation", "Other*", "SaaS"]},
|
| 208 |
+
"Customer Type": {"num_categories": 4, "categories": ["B2B", "B2C", "Both", "Other*"]},
|
| 209 |
+
"grouped_conversion_type": {"num_categories": 6, "categories": ["Direct Purchase", "High-Intent Lead Gen", "Info/Content Lead Gen", "Location Search", "Non-Profit/Community", "Other Conversion"]},
|
| 210 |
+
"grouped_industry": {"num_categories": 14, "categories": ["Automotive & Transportation", "B2B Services", "B2B Software & Tech", "Consumer Services", "Consumer Software & Apps", "Education", "Finance, Insurance & Real Estate", "Food, Hospitality & Travel", "Health & Wellness", "Industrial & Manufacturing", "Media & Entertainment", "Non-Profit & Government", "Other", "Retail & E-commerce"]},
|
| 211 |
+
"grouped_page_type": {"num_categories": 5, "categories": ["Awareness & Discovery", "Consideration & Evaluation", "Conversion", "Internal & Navigation", "Post-Conversion & Other"]}
|
| 212 |
+
}
|
| 213 |
+
with open(CAT_MAPPINGS_SAVE_PATH, 'w') as f:
|
| 214 |
+
json.dump(dummy_mappings, f, indent=2)
|
| 215 |
+
|
| 216 |
+
with open(CAT_MAPPINGS_SAVE_PATH, 'r') as f:
|
| 217 |
+
category_mappings = json.load(f)
|
| 218 |
+
|
| 219 |
+
# Load confidence scores from confidence_scores.js
|
| 220 |
+
def load_confidence_scores():
|
| 221 |
+
"""Load confidence scores from the JavaScript file"""
|
| 222 |
+
try:
|
| 223 |
+
result = subprocess.run([
|
| 224 |
+
'node', '-e',
|
| 225 |
+
'const conf = require("./confidence_scores.js"); console.log(JSON.stringify(conf.confidenceMapping));'
|
| 226 |
+
], capture_output=True, text=True, cwd='.')
|
| 227 |
+
|
| 228 |
+
if result.returncode == 0:
|
| 229 |
+
confidence_data = json.loads(result.stdout.strip())
|
| 230 |
+
print(f"✅ Successfully loaded {len(confidence_data)} confidence score combinations")
|
| 231 |
+
return confidence_data
|
| 232 |
+
else:
|
| 233 |
+
print(f"⚠️ Failed to load confidence scores: {result.stderr}")
|
| 234 |
+
return {}
|
| 235 |
+
except Exception as e:
|
| 236 |
+
print(f"⚠️ Error loading confidence scores: {e}")
|
| 237 |
+
return {}
|
| 238 |
+
|
| 239 |
+
# Load confidence scores
|
| 240 |
+
try:
|
| 241 |
+
confidence_scores = load_confidence_scores()
|
| 242 |
+
print(f"✅ Confidence scores loaded successfully: {len(confidence_scores)} combinations")
|
| 243 |
+
except Exception as e:
|
| 244 |
+
print(f"⚠️ Error loading confidence scores: {e}")
|
| 245 |
+
confidence_scores = {}
|
| 246 |
+
|
| 247 |
+
def get_confidence_data(business_model, customer_type, conversion_type, industry, page_type):
|
| 248 |
+
"""Get confidence data based on Industry + Page Type combination (more reliable than 5-feature combinations)"""
|
| 249 |
+
key = f"{industry}|{page_type}"
|
| 250 |
+
return confidence_scores.get(key, {
|
| 251 |
+
'accuracy': 0.5, # Default fallback
|
| 252 |
+
'count': 0,
|
| 253 |
+
'training_data_count': 0,
|
| 254 |
+
'correct_predictions': 0,
|
| 255 |
+
'actual_wins': 0,
|
| 256 |
+
'predicted_wins': 0
|
| 257 |
+
})
|
| 258 |
+
|
| 259 |
+
def image_to_base64(image):
|
| 260 |
+
"""Convert PIL image to base64 string for API"""
|
| 261 |
+
buffered = BytesIO()
|
| 262 |
+
image.save(buffered, format="JPEG")
|
| 263 |
+
img_str = base64.b64encode(buffered.getvalue()).decode()
|
| 264 |
+
return f"data:image/jpeg;base64,{img_str}"
|
| 265 |
+
|
| 266 |
+
def load_pattern_descriptions():
|
| 267 |
+
"""Load the pattern descriptions from patterbs.json"""
|
| 268 |
+
try:
|
| 269 |
+
with open('patterbs.json', 'r') as f:
|
| 270 |
+
pattern_data = json.load(f)
|
| 271 |
+
print(f"✅ Successfully loaded {len(pattern_data)} pattern descriptions")
|
| 272 |
+
return pattern_data
|
| 273 |
+
except Exception as e:
|
| 274 |
+
print(f"⚠️ Error loading pattern descriptions: {e}")
|
| 275 |
+
return []
|
| 276 |
+
|
| 277 |
+
# Load pattern descriptions once at startup
|
| 278 |
+
try:
|
| 279 |
+
pattern_descriptions = load_pattern_descriptions()
|
| 280 |
+
print(f"✅ Pattern descriptions loaded successfully: {len(pattern_descriptions)} patterns")
|
| 281 |
+
except Exception as e:
|
| 282 |
+
print(f"⚠️ Error loading pattern descriptions: {e}")
|
| 283 |
+
pattern_descriptions = []
|
| 284 |
+
|
| 285 |
+
def detect_pattern_with_gemini(control_image, variant_image):
|
| 286 |
+
"""Use Gemini Pro API to detect which A/B test pattern was applied by comparing control vs variant"""
|
| 287 |
+
if not GEMINI_API_KEY:
|
| 288 |
+
print("⚠️ GEMINI API KEY NOT FOUND! Set GEMINI_API_KEY in Hugging Face Spaces secrets.")
|
| 289 |
+
return "Button" # Use a real pattern as fallback
|
| 290 |
+
|
| 291 |
+
print(f"✅ Gemini API key found, making pattern detection request...")
|
| 292 |
+
|
| 293 |
+
if not pattern_descriptions:
|
| 294 |
+
print("⚠️ No pattern descriptions loaded. Using fallback pattern.")
|
| 295 |
+
return "Button" # Use a real pattern as fallback
|
| 296 |
+
|
| 297 |
+
try:
|
| 298 |
+
# Convert both images to base64 for comparison analysis
|
| 299 |
+
def image_to_gemini_format(image):
|
| 300 |
+
buffered = BytesIO()
|
| 301 |
+
image.save(buffered, format="JPEG")
|
| 302 |
+
return base64.b64encode(buffered.getvalue()).decode()
|
| 303 |
+
|
| 304 |
+
control_b64 = image_to_gemini_format(control_image)
|
| 305 |
+
variant_b64 = image_to_gemini_format(variant_image)
|
| 306 |
+
|
| 307 |
+
# Create focused prompt with short descriptions (more manageable for Gemini)
|
| 308 |
+
patterns_with_context = []
|
| 309 |
+
for i, pattern_info in enumerate(pattern_descriptions):
|
| 310 |
+
name = pattern_info['name']
|
| 311 |
+
short_desc = pattern_info.get('shortDescription', '').strip()
|
| 312 |
+
|
| 313 |
+
# Use only short description for more focused analysis
|
| 314 |
+
pattern_entry = f"{i+1}. **{name}**: {short_desc}"
|
| 315 |
+
patterns_with_context.append(pattern_entry)
|
| 316 |
+
|
| 317 |
+
patterns_text = "\n".join(patterns_with_context)
|
| 318 |
+
|
| 319 |
+
prompt = f'''You are an expert A/B testing visual analyst. Compare these CONTROL vs VARIANT images to identify the specific A/B test pattern.
|
| 320 |
+
|
| 321 |
+
VISUAL ANALYSIS INSTRUCTIONS:
|
| 322 |
+
1. **Form Over UI**: Look for a signup/contact form overlaid on top of dashboard/interface screenshots in the background
|
| 323 |
+
2. **Double Column Form**: Look for forms with fields arranged in two columns side-by-side (not overlaid on UI)
|
| 324 |
+
3. **CTA Changes**: Look for button color, size, text, or position differences
|
| 325 |
+
4. **Hero Changes**: Look for hero section layout, content, or image modifications
|
| 326 |
+
5. **Layout Changes**: Look for structural, spacing, or positioning differences
|
| 327 |
+
|
| 328 |
+
KEY VISUAL CUES TO IDENTIFY:
|
| 329 |
+
- **Form Over UI**: Form in foreground + blurred/visible interface/dashboard in background
|
| 330 |
+
- **Double Column Form**: Form fields arranged in 2 columns (firstname + lastname on same row)
|
| 331 |
+
- **Sticky Elements**: Fixed elements that stay visible while scrolling
|
| 332 |
+
- **Social Proof**: Reviews, testimonials, logos, trust badges
|
| 333 |
+
- **CTA Modifications**: Button styling, positioning, or messaging changes
|
| 334 |
+
|
| 335 |
+
CRITICAL: Compare CONTROL vs VARIANT to see what changed!
|
| 336 |
+
|
| 337 |
+
AVAILABLE PATTERNS:
|
| 338 |
+
{patterns_text}
|
| 339 |
+
|
| 340 |
+
RESPONSE RULES:
|
| 341 |
+
- You MUST pick ONE pattern from the list above
|
| 342 |
+
- Return ONLY the exact pattern name (no numbers, no quotes)
|
| 343 |
+
- Focus on the MAIN difference between control and variant
|
| 344 |
+
- If you see a form over interface/dashboard background, choose "Form Over UI"
|
| 345 |
+
- If you see side-by-side form fields, choose "Double Column Form"
|
| 346 |
+
|
| 347 |
+
Analyze the visual differences now and respond with the exact pattern name.'''
|
| 348 |
+
|
| 349 |
+
# Prepare Gemini Pro API request
|
| 350 |
+
headers = {
|
| 351 |
+
"Content-Type": "application/json"
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
# Gemini Pro request format with both images for comparison
|
| 355 |
+
data = {
|
| 356 |
+
"contents": [
|
| 357 |
+
{
|
| 358 |
+
"parts": [
|
| 359 |
+
{"text": prompt},
|
| 360 |
+
{
|
| 361 |
+
"inline_data": {
|
| 362 |
+
"mime_type": "image/jpeg",
|
| 363 |
+
"data": control_b64
|
| 364 |
+
}
|
| 365 |
+
},
|
| 366 |
+
{"text": "CONTROL IMAGE (Original) ↑"},
|
| 367 |
+
{
|
| 368 |
+
"inline_data": {
|
| 369 |
+
"mime_type": "image/jpeg",
|
| 370 |
+
"data": variant_b64
|
| 371 |
+
}
|
| 372 |
+
},
|
| 373 |
+
{"text": "VARIANT IMAGE (Modified) ↑\n\nAnalyze the differences between these two images to identify the A/B test pattern."}
|
| 374 |
+
]
|
| 375 |
+
}
|
| 376 |
+
],
|
| 377 |
+
"generationConfig": {
|
| 378 |
+
"temperature": 0.2, # Slightly higher for better pattern selection
|
| 379 |
+
"maxOutputTokens": 100, # Sufficient for pattern names
|
| 380 |
+
"topP": 0.9,
|
| 381 |
+
"topK": 50
|
| 382 |
+
},
|
| 383 |
+
"safetySettings": [
|
| 384 |
+
{
|
| 385 |
+
"category": "HARM_CATEGORY_HARASSMENT",
|
| 386 |
+
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
"category": "HARM_CATEGORY_HATE_SPEECH",
|
| 390 |
+
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 391 |
+
},
|
| 392 |
+
{
|
| 393 |
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 394 |
+
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 395 |
+
},
|
| 396 |
+
{
|
| 397 |
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 398 |
+
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 399 |
+
}
|
| 400 |
+
]
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
# Make API call to Gemini Pro
|
| 404 |
+
url = f"{GEMINI_API_URL}?key={GEMINI_API_KEY}"
|
| 405 |
+
print(f"🚀 Sending request to Gemini Pro API...")
|
| 406 |
+
response = requests.post(url, headers=headers, json=data, timeout=30)
|
| 407 |
+
print(f"📡 Gemini response status: {response.status_code}")
|
| 408 |
+
response.raise_for_status()
|
| 409 |
+
|
| 410 |
+
result = response.json()
|
| 411 |
+
print(f"🎯 Gemini response received, parsing pattern...")
|
| 412 |
+
|
| 413 |
+
# Extract the generated text from Gemini response
|
| 414 |
+
if 'candidates' in result and len(result['candidates']) > 0:
|
| 415 |
+
candidate = result['candidates'][0]
|
| 416 |
+
if 'content' in candidate and 'parts' in candidate['content']:
|
| 417 |
+
content = candidate['content']['parts'][0]['text'].strip()
|
| 418 |
+
print(f"🤖 Gemini raw response: '{content}'")
|
| 419 |
+
|
| 420 |
+
# Clean the response to get just the pattern name
|
| 421 |
+
detected_pattern = content.strip().strip('"').strip("'").strip('.')
|
| 422 |
+
print(f"🎯 Cleaned pattern: '{detected_pattern}'")
|
| 423 |
+
|
| 424 |
+
# Validate against pattern names from descriptions
|
| 425 |
+
pattern_names = [p['name'] for p in pattern_descriptions]
|
| 426 |
+
|
| 427 |
+
# Validate that the detected pattern is in our list
|
| 428 |
+
if detected_pattern in pattern_names:
|
| 429 |
+
print(f"🎯 Gemini Pro detected pattern: {detected_pattern}")
|
| 430 |
+
return detected_pattern
|
| 431 |
+
else:
|
| 432 |
+
print(f"⚠️ Invalid pattern detected: '{detected_pattern}', searching for best match")
|
| 433 |
+
|
| 434 |
+
# Enhanced matching logic - try multiple approaches
|
| 435 |
+
best_match = None
|
| 436 |
+
|
| 437 |
+
# 1. Try exact partial match
|
| 438 |
+
for pattern_info in pattern_descriptions:
|
| 439 |
+
pattern_name = pattern_info['name']
|
| 440 |
+
if pattern_name.lower() in detected_pattern.lower():
|
| 441 |
+
best_match = pattern_name
|
| 442 |
+
print(f"🎯 Found exact partial match: {pattern_name}")
|
| 443 |
+
break
|
| 444 |
+
|
| 445 |
+
# 2. Try reverse partial match
|
| 446 |
+
if not best_match:
|
| 447 |
+
for pattern_info in pattern_descriptions:
|
| 448 |
+
pattern_name = pattern_info['name']
|
| 449 |
+
if detected_pattern.lower() in pattern_name.lower():
|
| 450 |
+
best_match = pattern_name
|
| 451 |
+
print(f"🎯 Found reverse partial match: {pattern_name}")
|
| 452 |
+
break
|
| 453 |
+
|
| 454 |
+
# 3. Try word-based matching
|
| 455 |
+
if not best_match:
|
| 456 |
+
detected_words = set(detected_pattern.lower().split())
|
| 457 |
+
best_score = 0
|
| 458 |
+
for pattern_info in pattern_descriptions:
|
| 459 |
+
pattern_name = pattern_info['name']
|
| 460 |
+
pattern_words = set(pattern_name.lower().split())
|
| 461 |
+
score = len(detected_words.intersection(pattern_words))
|
| 462 |
+
if score > best_score:
|
| 463 |
+
best_score = score
|
| 464 |
+
best_match = pattern_name
|
| 465 |
+
|
| 466 |
+
if best_match and best_score > 0:
|
| 467 |
+
print(f"🎯 Found word-based match: {best_match} (score: {best_score})")
|
| 468 |
+
|
| 469 |
+
# 4. If still no match, use first pattern as fallback (force valid pattern)
|
| 470 |
+
if not best_match:
|
| 471 |
+
best_match = pattern_descriptions[0]['name']
|
| 472 |
+
print(f"⚠️ No good match found, using first pattern as fallback: {best_match}")
|
| 473 |
+
|
| 474 |
+
return best_match
|
| 475 |
+
else:
|
| 476 |
+
print(f"⚠️ Unexpected Gemini response format: {result}")
|
| 477 |
+
return pattern_descriptions[0]['name'] if pattern_descriptions else "Button"
|
| 478 |
+
else:
|
| 479 |
+
print(f"⚠️ No candidates in Gemini response: {result}")
|
| 480 |
+
return pattern_descriptions[0]['name'] if pattern_descriptions else "Button"
|
| 481 |
+
|
| 482 |
+
except Exception as e:
|
| 483 |
+
print(f"❌ GEMINI API ERROR: {e}")
|
| 484 |
+
print(f"🔍 Error type: {type(e).__name__}")
|
| 485 |
+
if hasattr(e, 'response') and e.response is not None:
|
| 486 |
+
try:
|
| 487 |
+
print(f"📡 Response status: {e.response.status_code}")
|
| 488 |
+
print(f"📡 Response text: {e.response.text[:200]}...")
|
| 489 |
+
except AttributeError:
|
| 490 |
+
print("📡 Response object has no status_code/text attributes")
|
| 491 |
+
print("🔄 Using fallback pattern due to API error")
|
| 492 |
+
return pattern_descriptions[0]['name'] if pattern_descriptions else "Button"
|
| 493 |
+
|
| 494 |
+
def analyze_images_with_perplexity(control_image, variant_image):
|
| 495 |
+
"""Use Perplexity API to analyze images and categorize them"""
|
| 496 |
+
if not PERPLEXITY_API_KEY:
|
| 497 |
+
print("⚠️ PERPLEXITY API KEY NOT FOUND! Set PERPLEXITY_API_KEY in Hugging Face Spaces secrets.")
|
| 498 |
+
return {
|
| 499 |
+
"business_model": "Other*",
|
| 500 |
+
"customer_type": "Other*",
|
| 501 |
+
"conversion_type": "Other Conversion",
|
| 502 |
+
"industry": "Other",
|
| 503 |
+
"page_type": "Awareness & Discovery"
|
| 504 |
+
}
|
| 505 |
+
|
| 506 |
+
print(f"✅ Perplexity API key found, making categorization request...")
|
| 507 |
+
|
| 508 |
+
try:
|
| 509 |
+
# Convert images to base64
|
| 510 |
+
control_b64 = image_to_base64(control_image)
|
| 511 |
+
variant_b64 = image_to_base64(variant_image)
|
| 512 |
+
|
| 513 |
+
# Create enhanced prompt for Sonar Reasoning Pro's advanced analysis
|
| 514 |
+
prompt = f'''You are an expert A/B testing analyst. Analyze these two A/B test images (control and variant) using advanced multi-step reasoning to categorize them accurately.
|
| 515 |
+
|
| 516 |
+
CONTROL IMAGE: [Image 1]
|
| 517 |
+
VARIANT IMAGE: [Image 2]
|
| 518 |
+
|
| 519 |
+
ANALYSIS FRAMEWORK:
|
| 520 |
+
1. First, examine the visual elements, layout, colors, and UI components
|
| 521 |
+
2. Then, analyze any visible text, CTAs, forms, and messaging
|
| 522 |
+
3. Consider the overall user experience and conversion flow
|
| 523 |
+
4. Evaluate the business context and target audience indicators
|
| 524 |
+
5. Finally, match to the most appropriate categories
|
| 525 |
+
|
| 526 |
+
Use your advanced reasoning capabilities to select the BEST MATCH for each category:
|
| 527 |
+
|
| 528 |
+
**Business Model:**
|
| 529 |
+
- E-Commerce
|
| 530 |
+
- Lead Generation
|
| 531 |
+
- Other*
|
| 532 |
+
- SaaS
|
| 533 |
+
|
| 534 |
+
**Customer Type:**
|
| 535 |
+
- B2B
|
| 536 |
+
- B2C
|
| 537 |
+
- Both
|
| 538 |
+
- Other*
|
| 539 |
+
|
| 540 |
+
**Conversion Type:**
|
| 541 |
+
- Direct Purchase
|
| 542 |
+
- High-Intent Lead Gen
|
| 543 |
+
- Info/Content Lead Gen
|
| 544 |
+
- Location Search
|
| 545 |
+
- Non-Profit/Community
|
| 546 |
+
- Other Conversion
|
| 547 |
+
|
| 548 |
+
**Industry:**
|
| 549 |
+
- Automotive & Transportation
|
| 550 |
+
- B2B Services
|
| 551 |
+
- B2B Software & Tech
|
| 552 |
+
- Consumer Services
|
| 553 |
+
- Consumer Software & Apps
|
| 554 |
+
- Education
|
| 555 |
+
- Finance, Insurance & Real Estate
|
| 556 |
+
- Food, Hospitality & Travel
|
| 557 |
+
- Health & Wellness
|
| 558 |
+
- Industrial & Manufacturing
|
| 559 |
+
- Media & Entertainment
|
| 560 |
+
- Non-Profit & Government
|
| 561 |
+
- Other
|
| 562 |
+
- Retail & E-commerce
|
| 563 |
+
|
| 564 |
+
**Page Type:**
|
| 565 |
+
- Awareness & Discovery
|
| 566 |
+
- Consideration & Evaluation
|
| 567 |
+
- Conversion
|
| 568 |
+
- Internal & Navigation
|
| 569 |
+
- Post-Conversion & Other
|
| 570 |
+
|
| 571 |
+
Return your analysis in this EXACT JSON format (no additional text):
|
| 572 |
+
{{
|
| 573 |
+
"business_model": "selected_option",
|
| 574 |
+
"customer_type": "selected_option",
|
| 575 |
+
"conversion_type": "selected_option",
|
| 576 |
+
"industry": "selected_option",
|
| 577 |
+
"page_type": "selected_option"
|
| 578 |
+
}}'''
|
| 579 |
+
|
| 580 |
+
# Make API call to Perplexity
|
| 581 |
+
headers = {
|
| 582 |
+
"Authorization": f"Bearer {PERPLEXITY_API_KEY}",
|
| 583 |
+
"Content-Type": "application/json"
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
data = {
|
| 587 |
+
"model": "sonar-reasoning-pro",
|
| 588 |
+
"messages": [
|
| 589 |
+
{
|
| 590 |
+
"role": "user",
|
| 591 |
+
"content": [
|
| 592 |
+
{"type": "text", "text": prompt},
|
| 593 |
+
{"type": "image_url", "image_url": {"url": control_b64}},
|
| 594 |
+
{"type": "image_url", "image_url": {"url": variant_b64}}
|
| 595 |
+
]
|
| 596 |
+
}
|
| 597 |
+
],
|
| 598 |
+
"max_tokens": 800,
|
| 599 |
+
"temperature": 0.1
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
print(f"🚀 Sending request to Perplexity API...")
|
| 603 |
+
response = requests.post(PERPLEXITY_API_URL, headers=headers, json=data, timeout=30)
|
| 604 |
+
print(f"📡 Perplexity response status: {response.status_code}")
|
| 605 |
+
response.raise_for_status()
|
| 606 |
+
|
| 607 |
+
result = response.json()
|
| 608 |
+
print(f"📋 Perplexity response received, parsing content...")
|
| 609 |
+
content = result['choices'][0]['message']['content']
|
| 610 |
+
print(f"🤖 Perplexity raw response: {content[:200]}...") # First 200 chars
|
| 611 |
+
|
| 612 |
+
# Parse JSON response - Sonar Reasoning Pro outputs <think> section followed by JSON
|
| 613 |
+
try:
|
| 614 |
+
# Remove the <think> section if present (sonar-reasoning-pro specific)
|
| 615 |
+
if "<think>" in content and "</think>" in content:
|
| 616 |
+
# Find the end of the think section and get content after it
|
| 617 |
+
think_end = content.find("</think>")
|
| 618 |
+
content_after_think = content[think_end + 8:].strip()
|
| 619 |
+
print(f"🧠 AI reasoning detected, extracting JSON from {len(content_after_think)} chars")
|
| 620 |
+
else:
|
| 621 |
+
content_after_think = content
|
| 622 |
+
|
| 623 |
+
# Extract JSON from response
|
| 624 |
+
json_start = content_after_think.find('{')
|
| 625 |
+
json_end = content_after_think.rfind('}') + 1
|
| 626 |
+
|
| 627 |
+
if json_start == -1 or json_end == 0:
|
| 628 |
+
raise ValueError("No JSON found in response")
|
| 629 |
+
|
| 630 |
+
json_str = content_after_think[json_start:json_end]
|
| 631 |
+
|
| 632 |
+
categorization = json.loads(json_str)
|
| 633 |
+
print(f"🤖 Sonar Reasoning Pro categorization: {categorization}")
|
| 634 |
+
return categorization
|
| 635 |
+
|
| 636 |
+
except (json.JSONDecodeError, ValueError) as e:
|
| 637 |
+
print(f"❌ FAILED TO PARSE PERPLEXITY RESPONSE: {e}")
|
| 638 |
+
print(f"Raw content (first 500 chars): {content[:500]}...")
|
| 639 |
+
print("🔄 Using fallback categorization due to parsing error")
|
| 640 |
+
raise
|
| 641 |
+
|
| 642 |
+
except Exception as e:
|
| 643 |
+
print(f"❌ PERPLEXITY API ERROR: {e}")
|
| 644 |
+
print(f"🔍 Error type: {type(e).__name__}")
|
| 645 |
+
if hasattr(e, 'response') and e.response is not None:
|
| 646 |
+
try:
|
| 647 |
+
print(f"📡 Response status: {e.response.status_code}")
|
| 648 |
+
print(f"📡 Response text: {e.response.text[:200]}...")
|
| 649 |
+
except AttributeError:
|
| 650 |
+
print("📡 Response object has no status_code/text attributes")
|
| 651 |
+
print("🔄 Using fallback categorization due to API error")
|
| 652 |
+
# Return fallback categorization
|
| 653 |
+
return {
|
| 654 |
+
"business_model": "Other*",
|
| 655 |
+
"customer_type": "Other*",
|
| 656 |
+
"conversion_type": "Other Conversion",
|
| 657 |
+
"industry": "Other",
|
| 658 |
+
"page_type": "Awareness & Discovery"
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
# Instantiate the model with the loaded mappings
|
| 662 |
+
model = SupervisedSiameseMultimodal(
|
| 663 |
+
VISION_MODEL_NAME, TEXT_MODEL_NAME, category_mappings, CATEGORICAL_EMBEDDING_DIMS
|
| 664 |
+
)
|
| 665 |
+
|
| 666 |
+
# Download model from Hugging Face Model Hub
|
| 667 |
+
def download_model_from_hub():
|
| 668 |
+
"""Download model and mappings from Hugging Face Model Hub"""
|
| 669 |
+
try:
|
| 670 |
+
print(f"📥 Downloading GGG model from Hugging Face Model Hub: {HF_MODEL_REPO}")
|
| 671 |
+
|
| 672 |
+
# Download model file
|
| 673 |
+
model_path = hf_hub_download(
|
| 674 |
+
repo_id=HF_MODEL_REPO,
|
| 675 |
+
filename=HF_MODEL_FILENAME,
|
| 676 |
+
cache_dir=MODEL_DIR
|
| 677 |
+
)
|
| 678 |
+
print(f"✅ Model downloaded to: {model_path}")
|
| 679 |
+
|
| 680 |
+
# Download category mappings if not exists locally
|
| 681 |
+
if not os.path.exists(CAT_MAPPINGS_SAVE_PATH):
|
| 682 |
+
try:
|
| 683 |
+
mappings_path = hf_hub_download(
|
| 684 |
+
repo_id=HF_MODEL_REPO,
|
| 685 |
+
filename=HF_MAPPINGS_FILENAME,
|
| 686 |
+
cache_dir=MODEL_DIR
|
| 687 |
+
)
|
| 688 |
+
print(f"✅ Category mappings downloaded to: {mappings_path}")
|
| 689 |
+
|
| 690 |
+
# Copy to expected location
|
| 691 |
+
import shutil
|
| 692 |
+
shutil.copy(mappings_path, CAT_MAPPINGS_SAVE_PATH)
|
| 693 |
+
except Exception as e:
|
| 694 |
+
print(f"⚠️ Could not download mappings from hub: {e}")
|
| 695 |
+
|
| 696 |
+
return model_path
|
| 697 |
+
|
| 698 |
+
except Exception as e:
|
| 699 |
+
print(f"⚠️ Error downloading from Model Hub: {e}")
|
| 700 |
+
print(f"🔧 Creating dummy weights for demo...")
|
| 701 |
+
torch.save(model.state_dict(), MODEL_SAVE_PATH)
|
| 702 |
+
return MODEL_SAVE_PATH
|
| 703 |
+
|
| 704 |
+
# Download or use local model
|
| 705 |
+
if not os.path.exists(MODEL_SAVE_PATH):
|
| 706 |
+
model_path = download_model_from_hub()
|
| 707 |
+
else:
|
| 708 |
+
model_path = MODEL_SAVE_PATH
|
| 709 |
+
print(f"✅ Using local GGG model at {MODEL_SAVE_PATH}")
|
| 710 |
+
|
| 711 |
+
# Load the weights
|
| 712 |
+
try:
|
| 713 |
+
print(f"🚀 Loading GGG model weights from {model_path}")
|
| 714 |
+
state_dict = torch.load(model_path, map_location=device)
|
| 715 |
+
model.load_state_dict(state_dict)
|
| 716 |
+
print("✅ Successfully loaded GGG model weights from Hugging Face Model Hub")
|
| 717 |
+
except Exception as e:
|
| 718 |
+
print(f"⚠️ Error loading model weights: {e}")
|
| 719 |
+
print("🔧 Using initialized weights for demo...")
|
| 720 |
+
|
| 721 |
+
model.to(device)
|
| 722 |
+
model.eval()
|
| 723 |
+
|
| 724 |
+
# Warm up the model with a dummy forward pass for better performance
|
| 725 |
+
if torch.cuda.is_available():
|
| 726 |
+
with torch.no_grad():
|
| 727 |
+
dummy_c_pix = torch.randn(1, 3, 224, 224).to(device)
|
| 728 |
+
dummy_v_pix = torch.randn(1, 3, 224, 224).to(device)
|
| 729 |
+
dummy_c_tok = torch.randint(0, 1000, (1, MAX_TEXT_LENGTH)).to(device)
|
| 730 |
+
dummy_c_attn = torch.ones(1, MAX_TEXT_LENGTH).to(device)
|
| 731 |
+
dummy_v_tok = torch.randint(0, 1000, (1, MAX_TEXT_LENGTH)).to(device)
|
| 732 |
+
dummy_v_attn = torch.ones(1, MAX_TEXT_LENGTH).to(device)
|
| 733 |
+
dummy_cat_feats = torch.randint(0, 2, (1, len(CATEGORICAL_FEATURES))).to(device)
|
| 734 |
+
|
| 735 |
+
_ = model(
|
| 736 |
+
c_pix=dummy_c_pix, v_pix=dummy_v_pix,
|
| 737 |
+
c_tok=dummy_c_tok, c_attn=dummy_c_attn,
|
| 738 |
+
v_tok=dummy_v_tok, v_attn=dummy_v_attn,
|
| 739 |
+
cat_feats=dummy_cat_feats
|
| 740 |
+
)
|
| 741 |
+
print("🔥 Model warmed up successfully!")
|
| 742 |
+
|
| 743 |
+
# Load the processors for images and text
|
| 744 |
+
image_processor = AutoProcessor.from_pretrained(VISION_MODEL_NAME)
|
| 745 |
+
tokenizer = AutoTokenizer.from_pretrained(TEXT_MODEL_NAME)
|
| 746 |
+
|
| 747 |
+
print("✅ Model and processors loaded successfully.")
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
# --- 4. Prediction Functions ---
|
| 751 |
+
|
| 752 |
+
def get_image_path_from_url(image_url: str, base_dir: str) -> str | None:
|
| 753 |
+
"""Constructs a local image path from a URL-like string."""
|
| 754 |
+
try:
|
| 755 |
+
stem = os.path.splitext(os.path.basename(str(image_url)))[0]
|
| 756 |
+
return os.path.join(base_dir, f"{stem}.jpeg")
|
| 757 |
+
except (TypeError, ValueError):
|
| 758 |
+
return None
|
| 759 |
+
|
| 760 |
+
@spaces.GPU(duration=180) # Extended duration for maximum concurrent load
|
| 761 |
+
def predict_with_auto_categorization(control_image, variant_image):
|
| 762 |
+
"""Auto-categorize images using Perplexity API and make prediction"""
|
| 763 |
+
if control_image is None or variant_image is None:
|
| 764 |
+
return {"Error": 1.0, "Please upload both images": 0.0}
|
| 765 |
+
|
| 766 |
+
start_time = time.time()
|
| 767 |
+
|
| 768 |
+
# Convert numpy arrays to PIL Images
|
| 769 |
+
c_img = Image.fromarray(control_image).convert("RGB")
|
| 770 |
+
v_img = Image.fromarray(variant_image).convert("RGB")
|
| 771 |
+
|
| 772 |
+
# Run parallel API calls for categorization and pattern detection
|
| 773 |
+
print("🤖 Running parallel AI analysis...")
|
| 774 |
+
print("📋 Task 1: Categorizing business context (Perplexity Sonar Reasoning Pro)...")
|
| 775 |
+
print("🎯 Task 2: Detecting A/B test pattern (Gemini Pro)...")
|
| 776 |
+
|
| 777 |
+
import concurrent.futures
|
| 778 |
+
|
| 779 |
+
# Run both API calls in parallel for faster processing
|
| 780 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
| 781 |
+
# Submit both tasks
|
| 782 |
+
categorization_future = executor.submit(analyze_images_with_perplexity, c_img, v_img)
|
| 783 |
+
pattern_future = executor.submit(detect_pattern_with_gemini, c_img, v_img)
|
| 784 |
+
|
| 785 |
+
# Wait for both to complete
|
| 786 |
+
categorization = categorization_future.result()
|
| 787 |
+
detected_pattern = pattern_future.result()
|
| 788 |
+
|
| 789 |
+
# Extract categories
|
| 790 |
+
business_model = categorization['business_model']
|
| 791 |
+
customer_type = categorization['customer_type']
|
| 792 |
+
conversion_type = categorization['conversion_type']
|
| 793 |
+
industry = categorization['industry']
|
| 794 |
+
page_type = categorization['page_type']
|
| 795 |
+
|
| 796 |
+
print(f"📋 Auto-detected categories: {business_model} | {customer_type} | {conversion_type} | {industry} | {page_type}")
|
| 797 |
+
print(f"🎯 Detected A/B test pattern: {detected_pattern}")
|
| 798 |
+
|
| 799 |
+
# Now run the normal prediction with auto-detected categories
|
| 800 |
+
prediction_result = predict_single(control_image, variant_image, business_model, customer_type, conversion_type, industry, page_type)
|
| 801 |
+
|
| 802 |
+
# Create comprehensive result with prediction, categorization, and pattern detection
|
| 803 |
+
enhanced_result = {
|
| 804 |
+
"🎯 Prediction Results": prediction_result,
|
| 805 |
+
"🤖 Auto-Detected Categories": {
|
| 806 |
+
"Business Model": business_model,
|
| 807 |
+
"Customer Type": customer_type,
|
| 808 |
+
"Conversion Type": conversion_type,
|
| 809 |
+
"Industry": industry,
|
| 810 |
+
"Page Type": page_type
|
| 811 |
+
},
|
| 812 |
+
"🎯 Detected A/B Test Pattern": {
|
| 813 |
+
"Pattern": detected_pattern,
|
| 814 |
+
"Description": f"The variant implements a '{detected_pattern}' modification"
|
| 815 |
+
},
|
| 816 |
+
"📊 Processing Info": {
|
| 817 |
+
"Total Processing Time": f"{time.time() - start_time:.2f}s",
|
| 818 |
+
"AI Categorization": "✅ Perplexity Sonar Reasoning Pro" if PERPLEXITY_API_KEY else "⚠️ Fallback Mode",
|
| 819 |
+
"Pattern Detection": "✅ Gemini Pro Vision" if GEMINI_API_KEY else "⚠️ Fallback Mode",
|
| 820 |
+
"Confidence Source": f"{industry} | {page_type}",
|
| 821 |
+
"Total Patterns Analyzed": len(pattern_descriptions) if pattern_descriptions else 0
|
| 822 |
+
}
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
return enhanced_result
|
| 826 |
+
|
| 827 |
+
@spaces.GPU(duration=180) # Extended duration for maximum concurrent load
|
| 828 |
+
def predict_single(control_image, variant_image, business_model, customer_type, conversion_type, industry, page_type):
|
| 829 |
+
"""Orchestrates the prediction for a single pair of images and features."""
|
| 830 |
+
if control_image is None or variant_image is None:
|
| 831 |
+
return {"Error": 1.0, "Please upload both images": 0.0}
|
| 832 |
+
|
| 833 |
+
start_time = time.time()
|
| 834 |
+
|
| 835 |
+
c_img = Image.fromarray(control_image).convert("RGB")
|
| 836 |
+
v_img = Image.fromarray(variant_image).convert("RGB")
|
| 837 |
+
|
| 838 |
+
# Extract OCR text from both images (this is crucial for model performance)
|
| 839 |
+
try:
|
| 840 |
+
c_text_str = pytesseract.image_to_string(c_img)
|
| 841 |
+
v_text_str = pytesseract.image_to_string(v_img)
|
| 842 |
+
print(f"📝 OCR extracted - Control: {len(c_text_str)} chars, Variant: {len(v_text_str)} chars")
|
| 843 |
+
except pytesseract.TesseractNotFoundError:
|
| 844 |
+
print("🛑 Tesseract is not installed or not in your PATH. Skipping OCR.")
|
| 845 |
+
c_text_str, v_text_str = "", ""
|
| 846 |
+
|
| 847 |
+
# Get confidence data for this combination
|
| 848 |
+
confidence_data = get_confidence_data(business_model, customer_type, conversion_type, industry, page_type)
|
| 849 |
+
|
| 850 |
+
with torch.no_grad():
|
| 851 |
+
c_pix = image_processor(images=c_img, return_tensors="pt").pixel_values.to(device)
|
| 852 |
+
v_pix = image_processor(images=v_img, return_tensors="pt").pixel_values.to(device)
|
| 853 |
+
|
| 854 |
+
# Process OCR text through the text model
|
| 855 |
+
c_text = tokenizer(c_text_str, padding='max_length', truncation=True, max_length=MAX_TEXT_LENGTH, return_tensors='pt').to(device)
|
| 856 |
+
v_text = tokenizer(v_text_str, padding='max_length', truncation=True, max_length=MAX_TEXT_LENGTH, return_tensors='pt').to(device)
|
| 857 |
+
|
| 858 |
+
cat_inputs = [business_model, customer_type, conversion_type, industry, page_type]
|
| 859 |
+
cat_codes = [category_mappings[name]['categories'].index(val) for name, val in zip(CATEGORICAL_FEATURES, cat_inputs)]
|
| 860 |
+
cat_feats = torch.tensor([cat_codes], dtype=torch.int64).to(device)
|
| 861 |
+
|
| 862 |
+
# Run the multimodal model prediction
|
| 863 |
+
logits = model(
|
| 864 |
+
c_pix=c_pix, v_pix=v_pix,
|
| 865 |
+
c_tok=c_text['input_ids'], c_attn=c_text['attention_mask'],
|
| 866 |
+
v_tok=v_text['input_ids'], v_attn=v_text['attention_mask'],
|
| 867 |
+
cat_feats=cat_feats
|
| 868 |
+
)
|
| 869 |
+
|
| 870 |
+
probability = torch.sigmoid(logits).item()
|
| 871 |
+
|
| 872 |
+
processing_time = time.time() - start_time
|
| 873 |
+
|
| 874 |
+
# Log GPU memory usage for monitoring
|
| 875 |
+
if torch.cuda.is_available():
|
| 876 |
+
gpu_memory = torch.cuda.memory_allocated() / 1024**3
|
| 877 |
+
print(f"🚀 Prediction completed in {processing_time:.2f}s | GPU Memory: {gpu_memory:.1f}GB")
|
| 878 |
+
else:
|
| 879 |
+
print(f"🚀 Prediction completed in {processing_time:.2f}s")
|
| 880 |
+
|
| 881 |
+
# Determine winner
|
| 882 |
+
winner = "VARIANT WINS" if probability > 0.5 else "CONTROL WINS"
|
| 883 |
+
confidence_percentage = confidence_data['accuracy'] * 100
|
| 884 |
+
|
| 885 |
+
# Create enhanced output with confidence scores and training data info
|
| 886 |
+
result = {
|
| 887 |
+
f"🏆 {winner}": f"{probability:.3f}",
|
| 888 |
+
f"📊 Model Confidence": f"{confidence_percentage:.1f}%",
|
| 889 |
+
f"📈 Training Data": f"{confidence_data['training_data_count']} samples",
|
| 890 |
+
f"✅ Historical Accuracy": f"{confidence_data['correct_predictions']}/{confidence_data['count']} correct",
|
| 891 |
+
f"🎯 Win/Loss Ratio": f"{confidence_data['actual_wins']} wins in {confidence_data['count']} tests"
|
| 892 |
+
}
|
| 893 |
+
|
| 894 |
+
return result
|
| 895 |
+
|
| 896 |
+
@spaces.GPU
|
| 897 |
+
def predict_batch(csv_path, control_img_dir, variant_img_dir, num_samples):
|
| 898 |
+
"""Handles batch prediction from a CSV file."""
|
| 899 |
+
if not all([csv_path, control_img_dir, variant_img_dir, num_samples]):
|
| 900 |
+
return pd.DataFrame({"Error": ["Please fill in all fields."]})
|
| 901 |
+
|
| 902 |
+
try:
|
| 903 |
+
df = pd.read_csv(csv_path)
|
| 904 |
+
except FileNotFoundError:
|
| 905 |
+
return pd.DataFrame({"Error": [f"CSV file not found at: {csv_path}"]})
|
| 906 |
+
except Exception as e:
|
| 907 |
+
return pd.DataFrame({"Error": [f"Failed to read CSV: {e}"]})
|
| 908 |
+
|
| 909 |
+
if num_samples > len(df):
|
| 910 |
+
print(f"⚠️ Requested {num_samples} samples, but CSV only has {len(df)} rows. Using all rows.")
|
| 911 |
+
num_samples = len(df)
|
| 912 |
+
|
| 913 |
+
sample_df = df.sample(n=num_samples, random_state=42)
|
| 914 |
+
results = []
|
| 915 |
+
|
| 916 |
+
for _, row in sample_df.iterrows():
|
| 917 |
+
try:
|
| 918 |
+
# Construct image paths
|
| 919 |
+
c_path = get_image_path_from_url(row[CONTROL_IMAGE_URL_COLUMN], control_img_dir)
|
| 920 |
+
v_path = get_image_path_from_url(row[VARIANT_IMAGE_URL_COLUMN], variant_img_dir)
|
| 921 |
+
|
| 922 |
+
if not c_path or not os.path.exists(c_path):
|
| 923 |
+
raise FileNotFoundError(f"Control image not found: {c_path}")
|
| 924 |
+
if not v_path or not os.path.exists(v_path):
|
| 925 |
+
raise FileNotFoundError(f"Variant image not found: {v_path}")
|
| 926 |
+
|
| 927 |
+
# Get categorical features from the row
|
| 928 |
+
cat_features_from_row = [row[f] for f in CATEGORICAL_FEATURES]
|
| 929 |
+
|
| 930 |
+
# Use the core prediction logic
|
| 931 |
+
prediction = predict_single(
|
| 932 |
+
control_image=np.array(Image.open(c_path)),
|
| 933 |
+
variant_image=np.array(Image.open(v_path)),
|
| 934 |
+
business_model=cat_features_from_row[0],
|
| 935 |
+
customer_type=cat_features_from_row[1],
|
| 936 |
+
conversion_type=cat_features_from_row[2],
|
| 937 |
+
industry=cat_features_from_row[3],
|
| 938 |
+
page_type=cat_features_from_row[4]
|
| 939 |
+
)
|
| 940 |
+
|
| 941 |
+
result_row = row.to_dict()
|
| 942 |
+
result_row['predicted_win_probability'] = prediction.get('Win', 0.0)
|
| 943 |
+
results.append(result_row)
|
| 944 |
+
|
| 945 |
+
except Exception as e:
|
| 946 |
+
print(f"🛑 Error processing row: {e}")
|
| 947 |
+
error_row = row.to_dict()
|
| 948 |
+
error_row['predicted_win_probability'] = f"ERROR: {e}"
|
| 949 |
+
results.append(error_row)
|
| 950 |
+
|
| 951 |
+
return pd.DataFrame(results)
|
| 952 |
+
|
| 953 |
+
|
| 954 |
+
# --- 5. Build the Gradio Interface ---
|
| 955 |
+
with gr.Blocks() as iface:
|
| 956 |
+
gr.Markdown("# 🚀 Multimodal A/B Test Predictor")
|
| 957 |
+
gr.Markdown("""
|
| 958 |
+
### Predict A/B test outcomes using:
|
| 959 |
+
- 🖼️ **Image Analysis**: Visual features from control & variant images
|
| 960 |
+
- 📝 **OCR Text Extraction**: Automatically extracts and analyzes text from images
|
| 961 |
+
- 📊 **Categorical Features**: Business context (industry, page type, etc.)
|
| 962 |
+
- 🎯 **Smart Confidence Scores**: Based on Industry + Page Type combinations with high sample counts
|
| 963 |
+
|
| 964 |
+
**Enhanced Reliability**: Confidence scores use Industry + Page Type combinations (avg 160 samples) instead of low-count 5-feature combinations!
|
| 965 |
+
""")
|
| 966 |
+
|
| 967 |
+
with gr.Tab("🤖 Smart Auto-Prediction"):
|
| 968 |
+
gr.Markdown("### 🚀 Dual-AI Powered Analysis")
|
| 969 |
+
gr.Markdown("Upload images and let **two specialized AIs** analyze your A/B test:")
|
| 970 |
+
|
| 971 |
+
with gr.Row():
|
| 972 |
+
with gr.Column():
|
| 973 |
+
auto_control_image = gr.Image(label="Control Image", type="numpy")
|
| 974 |
+
auto_variant_image = gr.Image(label="Variant Image", type="numpy")
|
| 975 |
+
with gr.Column():
|
| 976 |
+
gr.Markdown("### 🤖 Dual AI Analysis:")
|
| 977 |
+
gr.Markdown("**📋 Perplexity Sonar Reasoning Pro** (Business Context):")
|
| 978 |
+
gr.Markdown("- **Business Model** (E-Commerce, SaaS, etc.)")
|
| 979 |
+
gr.Markdown("- **Customer Type** (B2B, B2C, Both)")
|
| 980 |
+
gr.Markdown("- **Conversion Type** (Purchase, Lead Gen, etc.)")
|
| 981 |
+
gr.Markdown("- **Industry** (14 categories)")
|
| 982 |
+
gr.Markdown("- **Page Type** (5 categories)")
|
| 983 |
+
gr.Markdown("**🎯 Gemini Pro Vision** (Visual Pattern Detection):")
|
| 984 |
+
gr.Markdown("- **A/B Test Pattern** from 507 possible patterns")
|
| 985 |
+
gr.Markdown("- **Visual Change Analysis** (CTA, Copy, Layout, etc.)")
|
| 986 |
+
gr.Markdown("- **Superior visual understanding** for precise pattern detection")
|
| 987 |
+
|
| 988 |
+
auto_predict_btn = gr.Button("🤖 Auto-Analyze & Predict", variant="primary", size="lg")
|
| 989 |
+
auto_output_json = gr.JSON(label="🎯 AI Analysis & Prediction Results")
|
| 990 |
+
|
| 991 |
+
with gr.Tab("📋 Manual Selection"):
|
| 992 |
+
gr.Markdown("### Manual Category Selection")
|
| 993 |
+
gr.Markdown("Select categories manually if you prefer precise control.")
|
| 994 |
+
|
| 995 |
+
with gr.Row():
|
| 996 |
+
with gr.Column():
|
| 997 |
+
s_control_image = gr.Image(label="Control Image", type="numpy")
|
| 998 |
+
s_variant_image = gr.Image(label="Variant Image", type="numpy")
|
| 999 |
+
with gr.Column():
|
| 1000 |
+
s_business_model = gr.Dropdown(choices=category_mappings["Business Model"]['categories'], label="Business Model", value=category_mappings["Business Model"]['categories'][0])
|
| 1001 |
+
s_customer_type = gr.Dropdown(choices=category_mappings["Customer Type"]['categories'], label="Customer Type", value=category_mappings["Customer Type"]['categories'][0])
|
| 1002 |
+
s_conversion_type = gr.Dropdown(choices=category_mappings["grouped_conversion_type"]['categories'], label="Conversion Type", value=category_mappings["grouped_conversion_type"]['categories'][0])
|
| 1003 |
+
s_industry = gr.Dropdown(choices=category_mappings["grouped_industry"]['categories'], label="Industry", value=category_mappings["grouped_industry"]['categories'][0])
|
| 1004 |
+
s_page_type = gr.Dropdown(choices=category_mappings["grouped_page_type"]['categories'], label="Page Type", value=category_mappings["grouped_page_type"]['categories'][0])
|
| 1005 |
+
s_predict_btn = gr.Button("🔮 Predict A/B Test Winner", variant="secondary")
|
| 1006 |
+
s_output_label = gr.Label(num_top_classes=6, label="🎯 Prediction Results & Confidence Analysis")
|
| 1007 |
+
|
| 1008 |
+
with gr.Tab("Batch Prediction from CSV"):
|
| 1009 |
+
gr.Markdown("Provide paths to your data to get predictions for multiple random samples.")
|
| 1010 |
+
b_csv_path = gr.Textbox(label="Path to CSV file", placeholder="/path/to/your/data.csv")
|
| 1011 |
+
b_control_dir = gr.Textbox(label="Path to Control Images Folder", placeholder="/path/to/control_images/")
|
| 1012 |
+
b_variant_dir = gr.Textbox(label="Path to Variant Images Folder", placeholder="/path/to/variant_images/")
|
| 1013 |
+
b_num_samples = gr.Number(label="Number of random samples to predict", value=10)
|
| 1014 |
+
b_predict_btn = gr.Button("Run Batch Prediction")
|
| 1015 |
+
b_output_df = gr.DataFrame(label="Batch Prediction Results")
|
| 1016 |
+
|
| 1017 |
+
# Wire up the components
|
| 1018 |
+
auto_predict_btn.click(
|
| 1019 |
+
fn=predict_with_auto_categorization,
|
| 1020 |
+
inputs=[auto_control_image, auto_variant_image],
|
| 1021 |
+
outputs=auto_output_json
|
| 1022 |
+
)
|
| 1023 |
+
s_predict_btn.click(
|
| 1024 |
+
fn=predict_single,
|
| 1025 |
+
inputs=[s_control_image, s_variant_image, s_business_model, s_customer_type, s_conversion_type, s_industry, s_page_type],
|
| 1026 |
+
outputs=s_output_label
|
| 1027 |
+
)
|
| 1028 |
+
b_predict_btn.click(
|
| 1029 |
+
fn=predict_batch,
|
| 1030 |
+
inputs=[b_csv_path, b_control_dir, b_variant_dir, b_num_samples],
|
| 1031 |
+
outputs=b_output_df
|
| 1032 |
+
)
|
| 1033 |
+
|
| 1034 |
+
# Launch the application
|
| 1035 |
+
if __name__ == "__main__":
|
| 1036 |
+
# AGGRESSIVE optimization for 4x L4 GPU - push to maximum limits
|
| 1037 |
+
iface.queue(
|
| 1038 |
+
max_size=128, # Much larger queue for heavy concurrent load
|
| 1039 |
+
default_concurrency_limit=64 # Push all 4 GPUs to maximum capacity
|
| 1040 |
+
).launch(
|
| 1041 |
+
server_name="0.0.0.0",
|
| 1042 |
+
server_port=7860,
|
| 1043 |
+
show_error=True # Show detailed errors for debugging
|
| 1044 |
+
)
|
| 1045 |
+
|
|
@@ -0,0 +1,583 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Confidence scores for Industry + Page Type combinations
|
| 3 |
+
* Generated from holdout_set_statistics.csv (grouping_level = 2)
|
| 4 |
+
*
|
| 5 |
+
* Key format: "Industry|Page Type"
|
| 6 |
+
* Much higher sample counts and more reliable than 5-feature combinations
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
const confidenceMapping = {
|
| 10 |
+
"Automotive & Transportation|Awareness & Discovery": {
|
| 11 |
+
"accuracy": 0.6,
|
| 12 |
+
"count": 15,
|
| 13 |
+
"training_data_count": 135,
|
| 14 |
+
"correct_predictions": 9,
|
| 15 |
+
"actual_wins": 6,
|
| 16 |
+
"predicted_wins": 2
|
| 17 |
+
},
|
| 18 |
+
"Automotive & Transportation|Consideration & Evaluation": {
|
| 19 |
+
"accuracy": 0.667,
|
| 20 |
+
"count": 6,
|
| 21 |
+
"training_data_count": 54,
|
| 22 |
+
"correct_predictions": 4,
|
| 23 |
+
"actual_wins": 2,
|
| 24 |
+
"predicted_wins": 2
|
| 25 |
+
},
|
| 26 |
+
"Automotive & Transportation|Conversion": {
|
| 27 |
+
"accuracy": 1.0,
|
| 28 |
+
"count": 3,
|
| 29 |
+
"training_data_count": 27,
|
| 30 |
+
"correct_predictions": 3,
|
| 31 |
+
"actual_wins": 0,
|
| 32 |
+
"predicted_wins": 0
|
| 33 |
+
},
|
| 34 |
+
"Automotive & Transportation|Internal & Navigation": {
|
| 35 |
+
"accuracy": 0.571,
|
| 36 |
+
"count": 7,
|
| 37 |
+
"training_data_count": 63,
|
| 38 |
+
"correct_predictions": 4,
|
| 39 |
+
"actual_wins": 3,
|
| 40 |
+
"predicted_wins": 2
|
| 41 |
+
},
|
| 42 |
+
"B2B Services|Awareness & Discovery": {
|
| 43 |
+
"accuracy": 0.698,
|
| 44 |
+
"count": 483,
|
| 45 |
+
"training_data_count": 4347,
|
| 46 |
+
"correct_predictions": 337,
|
| 47 |
+
"actual_wins": 186,
|
| 48 |
+
"predicted_wins": 178
|
| 49 |
+
},
|
| 50 |
+
"B2B Services|Consideration & Evaluation": {
|
| 51 |
+
"accuracy": 0.657,
|
| 52 |
+
"count": 175,
|
| 53 |
+
"training_data_count": 1575,
|
| 54 |
+
"correct_predictions": 115,
|
| 55 |
+
"actual_wins": 82,
|
| 56 |
+
"predicted_wins": 78
|
| 57 |
+
},
|
| 58 |
+
"B2B Services|Conversion": {
|
| 59 |
+
"accuracy": 0.604,
|
| 60 |
+
"count": 53,
|
| 61 |
+
"training_data_count": 477,
|
| 62 |
+
"correct_predictions": 32,
|
| 63 |
+
"actual_wins": 26,
|
| 64 |
+
"predicted_wins": 23
|
| 65 |
+
},
|
| 66 |
+
"B2B Services|Internal & Navigation": {
|
| 67 |
+
"accuracy": 0.719,
|
| 68 |
+
"count": 139,
|
| 69 |
+
"training_data_count": 1251,
|
| 70 |
+
"correct_predictions": 100,
|
| 71 |
+
"actual_wins": 58,
|
| 72 |
+
"predicted_wins": 43
|
| 73 |
+
},
|
| 74 |
+
"B2B Services|Post-Conversion & Other": {
|
| 75 |
+
"accuracy": 0.571,
|
| 76 |
+
"count": 14,
|
| 77 |
+
"training_data_count": 126,
|
| 78 |
+
"correct_predictions": 8,
|
| 79 |
+
"actual_wins": 4,
|
| 80 |
+
"predicted_wins": 8
|
| 81 |
+
},
|
| 82 |
+
"B2B Software & Tech|Awareness & Discovery": {
|
| 83 |
+
"accuracy": 0.661,
|
| 84 |
+
"count": 1626,
|
| 85 |
+
"training_data_count": 14634,
|
| 86 |
+
"correct_predictions": 1074,
|
| 87 |
+
"actual_wins": 667,
|
| 88 |
+
"predicted_wins": 625
|
| 89 |
+
},
|
| 90 |
+
"B2B Software & Tech|Consideration & Evaluation": {
|
| 91 |
+
"accuracy": 0.617,
|
| 92 |
+
"count": 1046,
|
| 93 |
+
"training_data_count": 9414,
|
| 94 |
+
"correct_predictions": 645,
|
| 95 |
+
"actual_wins": 432,
|
| 96 |
+
"predicted_wins": 397
|
| 97 |
+
},
|
| 98 |
+
"B2B Software & Tech|Conversion": {
|
| 99 |
+
"accuracy": 0.647,
|
| 100 |
+
"count": 184,
|
| 101 |
+
"training_data_count": 1656,
|
| 102 |
+
"correct_predictions": 119,
|
| 103 |
+
"actual_wins": 71,
|
| 104 |
+
"predicted_wins": 74
|
| 105 |
+
},
|
| 106 |
+
"B2B Software & Tech|Internal & Navigation": {
|
| 107 |
+
"accuracy": 0.715,
|
| 108 |
+
"count": 376,
|
| 109 |
+
"training_data_count": 3384,
|
| 110 |
+
"correct_predictions": 269,
|
| 111 |
+
"actual_wins": 138,
|
| 112 |
+
"predicted_wins": 117
|
| 113 |
+
},
|
| 114 |
+
"B2B Software & Tech|Post-Conversion & Other": {
|
| 115 |
+
"accuracy": 0.78,
|
| 116 |
+
"count": 41,
|
| 117 |
+
"training_data_count": 369,
|
| 118 |
+
"correct_predictions": 32,
|
| 119 |
+
"actual_wins": 15,
|
| 120 |
+
"predicted_wins": 18
|
| 121 |
+
},
|
| 122 |
+
"Consumer Services|Awareness & Discovery": {
|
| 123 |
+
"accuracy": 0.723,
|
| 124 |
+
"count": 238,
|
| 125 |
+
"training_data_count": 2142,
|
| 126 |
+
"correct_predictions": 172,
|
| 127 |
+
"actual_wins": 97,
|
| 128 |
+
"predicted_wins": 85
|
| 129 |
+
},
|
| 130 |
+
"Consumer Services|Consideration & Evaluation": {
|
| 131 |
+
"accuracy": 0.592,
|
| 132 |
+
"count": 103,
|
| 133 |
+
"training_data_count": 927,
|
| 134 |
+
"correct_predictions": 61,
|
| 135 |
+
"actual_wins": 49,
|
| 136 |
+
"predicted_wins": 41
|
| 137 |
+
},
|
| 138 |
+
"Consumer Services|Conversion": {
|
| 139 |
+
"accuracy": 0.643,
|
| 140 |
+
"count": 42,
|
| 141 |
+
"training_data_count": 378,
|
| 142 |
+
"correct_predictions": 27,
|
| 143 |
+
"actual_wins": 12,
|
| 144 |
+
"predicted_wins": 13
|
| 145 |
+
},
|
| 146 |
+
"Consumer Services|Internal & Navigation": {
|
| 147 |
+
"accuracy": 0.607,
|
| 148 |
+
"count": 56,
|
| 149 |
+
"training_data_count": 504,
|
| 150 |
+
"correct_predictions": 34,
|
| 151 |
+
"actual_wins": 32,
|
| 152 |
+
"predicted_wins": 22
|
| 153 |
+
},
|
| 154 |
+
"Consumer Services|Post-Conversion & Other": {
|
| 155 |
+
"accuracy": 0.5,
|
| 156 |
+
"count": 2,
|
| 157 |
+
"training_data_count": 18,
|
| 158 |
+
"correct_predictions": 1,
|
| 159 |
+
"actual_wins": 1,
|
| 160 |
+
"predicted_wins": 0
|
| 161 |
+
},
|
| 162 |
+
"Consumer Software & Apps|Awareness & Discovery": {
|
| 163 |
+
"accuracy": 0.682,
|
| 164 |
+
"count": 22,
|
| 165 |
+
"training_data_count": 198,
|
| 166 |
+
"correct_predictions": 15,
|
| 167 |
+
"actual_wins": 5,
|
| 168 |
+
"predicted_wins": 8
|
| 169 |
+
},
|
| 170 |
+
"Consumer Software & Apps|Consideration & Evaluation": {
|
| 171 |
+
"accuracy": 0.9,
|
| 172 |
+
"count": 10,
|
| 173 |
+
"training_data_count": 90,
|
| 174 |
+
"correct_predictions": 9,
|
| 175 |
+
"actual_wins": 6,
|
| 176 |
+
"predicted_wins": 5
|
| 177 |
+
},
|
| 178 |
+
"Consumer Software & Apps|Conversion": {
|
| 179 |
+
"accuracy": 0.667,
|
| 180 |
+
"count": 15,
|
| 181 |
+
"training_data_count": 135,
|
| 182 |
+
"correct_predictions": 10,
|
| 183 |
+
"actual_wins": 5,
|
| 184 |
+
"predicted_wins": 6
|
| 185 |
+
},
|
| 186 |
+
"Consumer Software & Apps|Internal & Navigation": {
|
| 187 |
+
"accuracy": 0.2,
|
| 188 |
+
"count": 5,
|
| 189 |
+
"training_data_count": 45,
|
| 190 |
+
"correct_predictions": 1,
|
| 191 |
+
"actual_wins": 3,
|
| 192 |
+
"predicted_wins": 3
|
| 193 |
+
},
|
| 194 |
+
"Consumer Software & Apps|Post-Conversion & Other": {
|
| 195 |
+
"accuracy": 0.0,
|
| 196 |
+
"count": 1,
|
| 197 |
+
"training_data_count": 9,
|
| 198 |
+
"correct_predictions": 0,
|
| 199 |
+
"actual_wins": 1,
|
| 200 |
+
"predicted_wins": 0
|
| 201 |
+
},
|
| 202 |
+
"Education|Awareness & Discovery": {
|
| 203 |
+
"accuracy": 0.589,
|
| 204 |
+
"count": 409,
|
| 205 |
+
"training_data_count": 3681,
|
| 206 |
+
"correct_predictions": 241,
|
| 207 |
+
"actual_wins": 180,
|
| 208 |
+
"predicted_wins": 170
|
| 209 |
+
},
|
| 210 |
+
"Education|Consideration & Evaluation": {
|
| 211 |
+
"accuracy": 0.645,
|
| 212 |
+
"count": 183,
|
| 213 |
+
"training_data_count": 1647,
|
| 214 |
+
"correct_predictions": 118,
|
| 215 |
+
"actual_wins": 72,
|
| 216 |
+
"predicted_wins": 77
|
| 217 |
+
},
|
| 218 |
+
"Education|Conversion": {
|
| 219 |
+
"accuracy": 0.605,
|
| 220 |
+
"count": 43,
|
| 221 |
+
"training_data_count": 387,
|
| 222 |
+
"correct_predictions": 26,
|
| 223 |
+
"actual_wins": 16,
|
| 224 |
+
"predicted_wins": 17
|
| 225 |
+
},
|
| 226 |
+
"Education|Internal & Navigation": {
|
| 227 |
+
"accuracy": 0.661,
|
| 228 |
+
"count": 177,
|
| 229 |
+
"training_data_count": 1593,
|
| 230 |
+
"correct_predictions": 117,
|
| 231 |
+
"actual_wins": 70,
|
| 232 |
+
"predicted_wins": 62
|
| 233 |
+
},
|
| 234 |
+
"Education|Post-Conversion & Other": {
|
| 235 |
+
"accuracy": 0.308,
|
| 236 |
+
"count": 13,
|
| 237 |
+
"training_data_count": 117,
|
| 238 |
+
"correct_predictions": 4,
|
| 239 |
+
"actual_wins": 9,
|
| 240 |
+
"predicted_wins": 8
|
| 241 |
+
},
|
| 242 |
+
"Finance, Insurance & Real Estate|Awareness & Discovery": {
|
| 243 |
+
"accuracy": 0.662,
|
| 244 |
+
"count": 417,
|
| 245 |
+
"training_data_count": 3753,
|
| 246 |
+
"correct_predictions": 276,
|
| 247 |
+
"actual_wins": 172,
|
| 248 |
+
"predicted_wins": 147
|
| 249 |
+
},
|
| 250 |
+
"Finance, Insurance & Real Estate|Consideration & Evaluation": {
|
| 251 |
+
"accuracy": 0.596,
|
| 252 |
+
"count": 193,
|
| 253 |
+
"training_data_count": 1737,
|
| 254 |
+
"correct_predictions": 115,
|
| 255 |
+
"actual_wins": 82,
|
| 256 |
+
"predicted_wins": 78
|
| 257 |
+
},
|
| 258 |
+
"Finance, Insurance & Real Estate|Conversion": {
|
| 259 |
+
"accuracy": 0.615,
|
| 260 |
+
"count": 52,
|
| 261 |
+
"training_data_count": 468,
|
| 262 |
+
"correct_predictions": 32,
|
| 263 |
+
"actual_wins": 26,
|
| 264 |
+
"predicted_wins": 22
|
| 265 |
+
},
|
| 266 |
+
"Finance, Insurance & Real Estate|Internal & Navigation": {
|
| 267 |
+
"accuracy": 0.678,
|
| 268 |
+
"count": 177,
|
| 269 |
+
"training_data_count": 1593,
|
| 270 |
+
"correct_predictions": 120,
|
| 271 |
+
"actual_wins": 65,
|
| 272 |
+
"predicted_wins": 60
|
| 273 |
+
},
|
| 274 |
+
"Finance, Insurance & Real Estate|Post-Conversion & Other": {
|
| 275 |
+
"accuracy": 0.545,
|
| 276 |
+
"count": 22,
|
| 277 |
+
"training_data_count": 198,
|
| 278 |
+
"correct_predictions": 12,
|
| 279 |
+
"actual_wins": 13,
|
| 280 |
+
"predicted_wins": 7
|
| 281 |
+
},
|
| 282 |
+
"Food, Hospitality & Travel|Awareness & Discovery": {
|
| 283 |
+
"accuracy": 0.676,
|
| 284 |
+
"count": 293,
|
| 285 |
+
"training_data_count": 2637,
|
| 286 |
+
"correct_predictions": 198,
|
| 287 |
+
"actual_wins": 141,
|
| 288 |
+
"predicted_wins": 136
|
| 289 |
+
},
|
| 290 |
+
"Food, Hospitality & Travel|Consideration & Evaluation": {
|
| 291 |
+
"accuracy": 0.642,
|
| 292 |
+
"count": 159,
|
| 293 |
+
"training_data_count": 1431,
|
| 294 |
+
"correct_predictions": 102,
|
| 295 |
+
"actual_wins": 70,
|
| 296 |
+
"predicted_wins": 59
|
| 297 |
+
},
|
| 298 |
+
"Food, Hospitality & Travel|Conversion": {
|
| 299 |
+
"accuracy": 0.6,
|
| 300 |
+
"count": 60,
|
| 301 |
+
"training_data_count": 540,
|
| 302 |
+
"correct_predictions": 36,
|
| 303 |
+
"actual_wins": 31,
|
| 304 |
+
"predicted_wins": 27
|
| 305 |
+
},
|
| 306 |
+
"Food, Hospitality & Travel|Internal & Navigation": {
|
| 307 |
+
"accuracy": 0.63,
|
| 308 |
+
"count": 73,
|
| 309 |
+
"training_data_count": 657,
|
| 310 |
+
"correct_predictions": 46,
|
| 311 |
+
"actual_wins": 32,
|
| 312 |
+
"predicted_wins": 27
|
| 313 |
+
},
|
| 314 |
+
"Food, Hospitality & Travel|Post-Conversion & Other": {
|
| 315 |
+
"accuracy": 0.286,
|
| 316 |
+
"count": 7,
|
| 317 |
+
"training_data_count": 63,
|
| 318 |
+
"correct_predictions": 2,
|
| 319 |
+
"actual_wins": 2,
|
| 320 |
+
"predicted_wins": 5
|
| 321 |
+
},
|
| 322 |
+
"Health & Wellness|Awareness & Discovery": {
|
| 323 |
+
"accuracy": 0.631,
|
| 324 |
+
"count": 643,
|
| 325 |
+
"training_data_count": 5787,
|
| 326 |
+
"correct_predictions": 406,
|
| 327 |
+
"actual_wins": 262,
|
| 328 |
+
"predicted_wins": 249
|
| 329 |
+
},
|
| 330 |
+
"Health & Wellness|Consideration & Evaluation": {
|
| 331 |
+
"accuracy": 0.663,
|
| 332 |
+
"count": 389,
|
| 333 |
+
"training_data_count": 3501,
|
| 334 |
+
"correct_predictions": 258,
|
| 335 |
+
"actual_wins": 175,
|
| 336 |
+
"predicted_wins": 156
|
| 337 |
+
},
|
| 338 |
+
"Health & Wellness|Conversion": {
|
| 339 |
+
"accuracy": 0.632,
|
| 340 |
+
"count": 106,
|
| 341 |
+
"training_data_count": 954,
|
| 342 |
+
"correct_predictions": 67,
|
| 343 |
+
"actual_wins": 42,
|
| 344 |
+
"predicted_wins": 53
|
| 345 |
+
},
|
| 346 |
+
"Health & Wellness|Internal & Navigation": {
|
| 347 |
+
"accuracy": 0.654,
|
| 348 |
+
"count": 156,
|
| 349 |
+
"training_data_count": 1404,
|
| 350 |
+
"correct_predictions": 102,
|
| 351 |
+
"actual_wins": 72,
|
| 352 |
+
"predicted_wins": 58
|
| 353 |
+
},
|
| 354 |
+
"Health & Wellness|Post-Conversion & Other": {
|
| 355 |
+
"accuracy": 0.667,
|
| 356 |
+
"count": 12,
|
| 357 |
+
"training_data_count": 108,
|
| 358 |
+
"correct_predictions": 8,
|
| 359 |
+
"actual_wins": 5,
|
| 360 |
+
"predicted_wins": 3
|
| 361 |
+
},
|
| 362 |
+
"Industrial & Manufacturing|Awareness & Discovery": {
|
| 363 |
+
"accuracy": 0.573,
|
| 364 |
+
"count": 171,
|
| 365 |
+
"training_data_count": 1539,
|
| 366 |
+
"correct_predictions": 98,
|
| 367 |
+
"actual_wins": 75,
|
| 368 |
+
"predicted_wins": 82
|
| 369 |
+
},
|
| 370 |
+
"Industrial & Manufacturing|Consideration & Evaluation": {
|
| 371 |
+
"accuracy": 0.677,
|
| 372 |
+
"count": 93,
|
| 373 |
+
"training_data_count": 837,
|
| 374 |
+
"correct_predictions": 63,
|
| 375 |
+
"actual_wins": 34,
|
| 376 |
+
"predicted_wins": 32
|
| 377 |
+
},
|
| 378 |
+
"Industrial & Manufacturing|Conversion": {
|
| 379 |
+
"accuracy": 0.778,
|
| 380 |
+
"count": 18,
|
| 381 |
+
"training_data_count": 162,
|
| 382 |
+
"correct_predictions": 14,
|
| 383 |
+
"actual_wins": 6,
|
| 384 |
+
"predicted_wins": 10
|
| 385 |
+
},
|
| 386 |
+
"Industrial & Manufacturing|Internal & Navigation": {
|
| 387 |
+
"accuracy": 0.776,
|
| 388 |
+
"count": 67,
|
| 389 |
+
"training_data_count": 603,
|
| 390 |
+
"correct_predictions": 52,
|
| 391 |
+
"actual_wins": 25,
|
| 392 |
+
"predicted_wins": 28
|
| 393 |
+
},
|
| 394 |
+
"Industrial & Manufacturing|Post-Conversion & Other": {
|
| 395 |
+
"accuracy": 0.833,
|
| 396 |
+
"count": 6,
|
| 397 |
+
"training_data_count": 54,
|
| 398 |
+
"correct_predictions": 5,
|
| 399 |
+
"actual_wins": 4,
|
| 400 |
+
"predicted_wins": 5
|
| 401 |
+
},
|
| 402 |
+
"Media & Entertainment|Awareness & Discovery": {
|
| 403 |
+
"accuracy": 0.701,
|
| 404 |
+
"count": 251,
|
| 405 |
+
"training_data_count": 2259,
|
| 406 |
+
"correct_predictions": 176,
|
| 407 |
+
"actual_wins": 99,
|
| 408 |
+
"predicted_wins": 106
|
| 409 |
+
},
|
| 410 |
+
"Media & Entertainment|Consideration & Evaluation": {
|
| 411 |
+
"accuracy": 0.663,
|
| 412 |
+
"count": 95,
|
| 413 |
+
"training_data_count": 855,
|
| 414 |
+
"correct_predictions": 63,
|
| 415 |
+
"actual_wins": 28,
|
| 416 |
+
"predicted_wins": 32
|
| 417 |
+
},
|
| 418 |
+
"Media & Entertainment|Conversion": {
|
| 419 |
+
"accuracy": 0.792,
|
| 420 |
+
"count": 24,
|
| 421 |
+
"training_data_count": 216,
|
| 422 |
+
"correct_predictions": 19,
|
| 423 |
+
"actual_wins": 10,
|
| 424 |
+
"predicted_wins": 11
|
| 425 |
+
},
|
| 426 |
+
"Media & Entertainment|Internal & Navigation": {
|
| 427 |
+
"accuracy": 0.676,
|
| 428 |
+
"count": 68,
|
| 429 |
+
"training_data_count": 612,
|
| 430 |
+
"correct_predictions": 46,
|
| 431 |
+
"actual_wins": 24,
|
| 432 |
+
"predicted_wins": 18
|
| 433 |
+
},
|
| 434 |
+
"Media & Entertainment|Post-Conversion & Other": {
|
| 435 |
+
"accuracy": 0.6,
|
| 436 |
+
"count": 5,
|
| 437 |
+
"training_data_count": 45,
|
| 438 |
+
"correct_predictions": 3,
|
| 439 |
+
"actual_wins": 1,
|
| 440 |
+
"predicted_wins": 1
|
| 441 |
+
},
|
| 442 |
+
"Non-Profit & Government|Awareness & Discovery": {
|
| 443 |
+
"accuracy": 0.692,
|
| 444 |
+
"count": 107,
|
| 445 |
+
"training_data_count": 963,
|
| 446 |
+
"correct_predictions": 74,
|
| 447 |
+
"actual_wins": 36,
|
| 448 |
+
"predicted_wins": 37
|
| 449 |
+
},
|
| 450 |
+
"Non-Profit & Government|Consideration & Evaluation": {
|
| 451 |
+
"accuracy": 0.531,
|
| 452 |
+
"count": 32,
|
| 453 |
+
"training_data_count": 288,
|
| 454 |
+
"correct_predictions": 17,
|
| 455 |
+
"actual_wins": 12,
|
| 456 |
+
"predicted_wins": 11
|
| 457 |
+
},
|
| 458 |
+
"Non-Profit & Government|Conversion": {
|
| 459 |
+
"accuracy": 0.707,
|
| 460 |
+
"count": 92,
|
| 461 |
+
"training_data_count": 828,
|
| 462 |
+
"correct_predictions": 65,
|
| 463 |
+
"actual_wins": 29,
|
| 464 |
+
"predicted_wins": 22
|
| 465 |
+
},
|
| 466 |
+
"Non-Profit & Government|Internal & Navigation": {
|
| 467 |
+
"accuracy": 0.64,
|
| 468 |
+
"count": 50,
|
| 469 |
+
"training_data_count": 450,
|
| 470 |
+
"correct_predictions": 32,
|
| 471 |
+
"actual_wins": 23,
|
| 472 |
+
"predicted_wins": 21
|
| 473 |
+
},
|
| 474 |
+
"Non-Profit & Government|Post-Conversion & Other": {
|
| 475 |
+
"accuracy": 0.909,
|
| 476 |
+
"count": 11,
|
| 477 |
+
"training_data_count": 99,
|
| 478 |
+
"correct_predictions": 10,
|
| 479 |
+
"actual_wins": 4,
|
| 480 |
+
"predicted_wins": 3
|
| 481 |
+
},
|
| 482 |
+
"Other|Awareness & Discovery": {
|
| 483 |
+
"accuracy": 0.755,
|
| 484 |
+
"count": 53,
|
| 485 |
+
"training_data_count": 477,
|
| 486 |
+
"correct_predictions": 40,
|
| 487 |
+
"actual_wins": 24,
|
| 488 |
+
"predicted_wins": 21
|
| 489 |
+
},
|
| 490 |
+
"Other|Consideration & Evaluation": {
|
| 491 |
+
"accuracy": 0.385,
|
| 492 |
+
"count": 26,
|
| 493 |
+
"training_data_count": 234,
|
| 494 |
+
"correct_predictions": 10,
|
| 495 |
+
"actual_wins": 13,
|
| 496 |
+
"predicted_wins": 9
|
| 497 |
+
},
|
| 498 |
+
"Other|Conversion": {
|
| 499 |
+
"accuracy": 0.75,
|
| 500 |
+
"count": 4,
|
| 501 |
+
"training_data_count": 36,
|
| 502 |
+
"correct_predictions": 3,
|
| 503 |
+
"actual_wins": 2,
|
| 504 |
+
"predicted_wins": 3
|
| 505 |
+
},
|
| 506 |
+
"Other|Internal & Navigation": {
|
| 507 |
+
"accuracy": 0.4,
|
| 508 |
+
"count": 10,
|
| 509 |
+
"training_data_count": 90,
|
| 510 |
+
"correct_predictions": 4,
|
| 511 |
+
"actual_wins": 5,
|
| 512 |
+
"predicted_wins": 1
|
| 513 |
+
},
|
| 514 |
+
"Other|Post-Conversion & Other": {
|
| 515 |
+
"accuracy": 1.0,
|
| 516 |
+
"count": 1,
|
| 517 |
+
"training_data_count": 9,
|
| 518 |
+
"correct_predictions": 1,
|
| 519 |
+
"actual_wins": 1,
|
| 520 |
+
"predicted_wins": 1
|
| 521 |
+
},
|
| 522 |
+
"Retail & E-commerce|Awareness & Discovery": {
|
| 523 |
+
"accuracy": 0.645,
|
| 524 |
+
"count": 619,
|
| 525 |
+
"training_data_count": 5571,
|
| 526 |
+
"correct_predictions": 399,
|
| 527 |
+
"actual_wins": 309,
|
| 528 |
+
"predicted_wins": 239
|
| 529 |
+
},
|
| 530 |
+
"Retail & E-commerce|Consideration & Evaluation": {
|
| 531 |
+
"accuracy": 0.638,
|
| 532 |
+
"count": 718,
|
| 533 |
+
"training_data_count": 6462,
|
| 534 |
+
"correct_predictions": 458,
|
| 535 |
+
"actual_wins": 345,
|
| 536 |
+
"predicted_wins": 309
|
| 537 |
+
},
|
| 538 |
+
"Retail & E-commerce|Conversion": {
|
| 539 |
+
"accuracy": 0.661,
|
| 540 |
+
"count": 112,
|
| 541 |
+
"training_data_count": 1008,
|
| 542 |
+
"correct_predictions": 74,
|
| 543 |
+
"actual_wins": 58,
|
| 544 |
+
"predicted_wins": 60
|
| 545 |
+
},
|
| 546 |
+
"Retail & E-commerce|Internal & Navigation": {
|
| 547 |
+
"accuracy": 0.636,
|
| 548 |
+
"count": 154,
|
| 549 |
+
"training_data_count": 1386,
|
| 550 |
+
"correct_predictions": 98,
|
| 551 |
+
"actual_wins": 67,
|
| 552 |
+
"predicted_wins": 59
|
| 553 |
+
},
|
| 554 |
+
"Retail & E-commerce|Post-Conversion & Other": {
|
| 555 |
+
"accuracy": 1.0,
|
| 556 |
+
"count": 5,
|
| 557 |
+
"training_data_count": 45,
|
| 558 |
+
"correct_predictions": 5,
|
| 559 |
+
"actual_wins": 2,
|
| 560 |
+
"predicted_wins": 2
|
| 561 |
+
}
|
| 562 |
+
};
|
| 563 |
+
|
| 564 |
+
// Helper function to get confidence data for Industry + Page Type combination
|
| 565 |
+
function getConfidenceScore(industry, pageType) {
|
| 566 |
+
const key = industry + '|' + pageType;
|
| 567 |
+
return confidenceMapping[key] || {
|
| 568 |
+
accuracy: 0.5, // Default fallback
|
| 569 |
+
count: 0,
|
| 570 |
+
training_data_count: 0,
|
| 571 |
+
correct_predictions: 0,
|
| 572 |
+
actual_wins: 0,
|
| 573 |
+
predicted_wins: 0
|
| 574 |
+
};
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
// Export for use in other files
|
| 578 |
+
if (typeof module !== 'undefined' && module.exports) {
|
| 579 |
+
module.exports = {
|
| 580 |
+
confidenceMapping,
|
| 581 |
+
getConfidenceScore
|
| 582 |
+
};
|
| 583 |
+
}
|
|
@@ -0,0 +1,604 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* This file contains the metadata used for filtering in the search application.
|
| 3 |
+
* It is auto-generated by a Python script. Do not edit this file manually.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const pattern = [
|
| 7 |
+
"Access Wall",
|
| 8 |
+
"Accordion",
|
| 9 |
+
"Add / Change Delay",
|
| 10 |
+
"Add / Change Emphasis",
|
| 11 |
+
"Add Background Image",
|
| 12 |
+
"Add CTA",
|
| 13 |
+
"Add Details",
|
| 14 |
+
"Amazon A/B Testing",
|
| 15 |
+
"Anchoring",
|
| 16 |
+
"Animated CTA",
|
| 17 |
+
"Animation",
|
| 18 |
+
"Annotate UI",
|
| 19 |
+
"Attention Director",
|
| 20 |
+
"Audio Recording",
|
| 21 |
+
"Autofill",
|
| 22 |
+
"Autoplay Video",
|
| 23 |
+
"Autoplay Video Without Sound",
|
| 24 |
+
"Baseline - Other",
|
| 25 |
+
"Baseline Hero",
|
| 26 |
+
"Before and After",
|
| 27 |
+
"Benefit Button",
|
| 28 |
+
"Bestseller / Featured Products",
|
| 29 |
+
"Big Claim Headline",
|
| 30 |
+
"Bigger Product Images",
|
| 31 |
+
"Blog / Content Summary",
|
| 32 |
+
"Blurred Content",
|
| 33 |
+
"Bold-face Copy",
|
| 34 |
+
"Bottom Banner to Top Banner",
|
| 35 |
+
"Bottom Corner Pop-up",
|
| 36 |
+
"Breadcrumbs",
|
| 37 |
+
"Bullet Points",
|
| 38 |
+
"Bullet Points - Change Inclusions",
|
| 39 |
+
"Bullet Points - Emphasize Inclusions",
|
| 40 |
+
"Bullet Points - Inclusions",
|
| 41 |
+
"Bulletize Copy",
|
| 42 |
+
"Button",
|
| 43 |
+
"Button Positioning",
|
| 44 |
+
"Button to Radio Button",
|
| 45 |
+
"Buttonized CTA",
|
| 46 |
+
"Buttons - Image",
|
| 47 |
+
"CTA Activation",
|
| 48 |
+
"CTA Color Change",
|
| 49 |
+
"CTA Copy Change",
|
| 50 |
+
"CTA Positioning",
|
| 51 |
+
"CTA Section",
|
| 52 |
+
"CTA Size",
|
| 53 |
+
"Calculator",
|
| 54 |
+
"Calendar",
|
| 55 |
+
"Calendar Flexibility Button",
|
| 56 |
+
"Cart Contents Section",
|
| 57 |
+
"Case Studies",
|
| 58 |
+
"Change / Add Link to Product Pages",
|
| 59 |
+
"Change / Add Product/Plan Name",
|
| 60 |
+
"Change Advertisements / Ads",
|
| 61 |
+
"Change Attention Director",
|
| 62 |
+
"Change Background Image",
|
| 63 |
+
"Change CTA Link",
|
| 64 |
+
"Change CTA Shape",
|
| 65 |
+
"Change CTA to Hyperlink",
|
| 66 |
+
"Change Checkbox/Tickbox to Buttons/Dropdown",
|
| 67 |
+
"Change Copy Order",
|
| 68 |
+
"Change Copy on Image / Video Overlay",
|
| 69 |
+
"Change Default Currency",
|
| 70 |
+
"Change Default Filter/Sort View",
|
| 71 |
+
"Change Displayed / Listed Product / Category",
|
| 72 |
+
"Change Donation Amount",
|
| 73 |
+
"Change Error Message",
|
| 74 |
+
"Change FAQs",
|
| 75 |
+
"Change Features",
|
| 76 |
+
"Change Field Type",
|
| 77 |
+
"Change Font",
|
| 78 |
+
"Change Font Color",
|
| 79 |
+
"Change Form Field Label",
|
| 80 |
+
"Change Form Fields",
|
| 81 |
+
"Change GIF / Video / Animation",
|
| 82 |
+
"Change Hero Image",
|
| 83 |
+
"Change How It Works",
|
| 84 |
+
"Change Hyperlink Copy",
|
| 85 |
+
"Change Icon",
|
| 86 |
+
"Change Icon to Copy",
|
| 87 |
+
"Change Image",
|
| 88 |
+
"Change Image / Video Size",
|
| 89 |
+
"Change Image Order",
|
| 90 |
+
"Change Inclusions",
|
| 91 |
+
"Change Integrations",
|
| 92 |
+
"Change Link to Internal Pages",
|
| 93 |
+
"Change Listing Cards Layout / Design",
|
| 94 |
+
"Change Logo",
|
| 95 |
+
"Change Logos in Social Proof",
|
| 96 |
+
"Change Matrix Layout / Design",
|
| 97 |
+
"Change Menu",
|
| 98 |
+
"Change Modal",
|
| 99 |
+
"Change Payment Options",
|
| 100 |
+
"Change Price",
|
| 101 |
+
"Change Pricing Card Layout / Design",
|
| 102 |
+
"Change Product Image",
|
| 103 |
+
"Change Progress Timeline",
|
| 104 |
+
"Change Promotion",
|
| 105 |
+
"Change Qualifying Questions",
|
| 106 |
+
"Change Real People",
|
| 107 |
+
"Change Recommended Tags",
|
| 108 |
+
"Change Resources / Articles",
|
| 109 |
+
"Change Reviews Summary",
|
| 110 |
+
"Change Section",
|
| 111 |
+
"Change Section Order",
|
| 112 |
+
"Change Selection Buttons to Field",
|
| 113 |
+
"Change Statistics",
|
| 114 |
+
"Change Subscription / Trial / Promotion Duration",
|
| 115 |
+
"Change Tag Design/Copy",
|
| 116 |
+
"Change Testimonial",
|
| 117 |
+
"Change Trust Badges",
|
| 118 |
+
"Change USPs*",
|
| 119 |
+
"Change Upsell Product",
|
| 120 |
+
"Change Video Thumbnail Image",
|
| 121 |
+
"Chatbot",
|
| 122 |
+
"Checkbox / Tickbox",
|
| 123 |
+
"Clearbit Form",
|
| 124 |
+
"Clickable Icon / Image",
|
| 125 |
+
"Clickable Section",
|
| 126 |
+
"Color Change",
|
| 127 |
+
"Company Info",
|
| 128 |
+
"Comparison Matrix",
|
| 129 |
+
"Competitor Pricing",
|
| 130 |
+
"Concise Headline",
|
| 131 |
+
"Condensed List",
|
| 132 |
+
"Contact Number",
|
| 133 |
+
"Contact Us Section",
|
| 134 |
+
"Content to Tabs",
|
| 135 |
+
"Copy - Add",
|
| 136 |
+
"Copy Center-Aligned",
|
| 137 |
+
"Copy Change",
|
| 138 |
+
"Copy Positioning",
|
| 139 |
+
"Countdown Timer",
|
| 140 |
+
"Cross Sell",
|
| 141 |
+
"Crossed-out Features",
|
| 142 |
+
"Currency Font Size",
|
| 143 |
+
"Currency to Percentage Savings",
|
| 144 |
+
"Database Number",
|
| 145 |
+
"Decrease Number of Slides",
|
| 146 |
+
"Demo/Trial Duration",
|
| 147 |
+
"Disclaimer",
|
| 148 |
+
"Double Column Form",
|
| 149 |
+
"Download Method",
|
| 150 |
+
"Dropdown",
|
| 151 |
+
"Dual CTA",
|
| 152 |
+
"Dummy Category Name",
|
| 153 |
+
"Dummy Form Field",
|
| 154 |
+
"Dynamic/Animated Headline",
|
| 155 |
+
"Easing Product/Plan Selection",
|
| 156 |
+
"Email Plus CTA",
|
| 157 |
+
"Email Plus CTA in Nav",
|
| 158 |
+
"Email Subscription",
|
| 159 |
+
"Emphasize / Highlight Urgency",
|
| 160 |
+
"Emphasize Buttons",
|
| 161 |
+
"Emphasize CTA",
|
| 162 |
+
"Emphasize Cross Sell",
|
| 163 |
+
"Emphasize Form",
|
| 164 |
+
"Emphasize Guarantee",
|
| 165 |
+
"Emphasize Inclusions",
|
| 166 |
+
"Emphasize Interface Image",
|
| 167 |
+
"Emphasize Options",
|
| 168 |
+
"Emphasize Payment Options",
|
| 169 |
+
"Emphasize Pricing / Price",
|
| 170 |
+
"Emphasize Search Bar",
|
| 171 |
+
"Emphasize Social Proof",
|
| 172 |
+
"Emphasize Testimonials",
|
| 173 |
+
"Exit Modal",
|
| 174 |
+
"Explainer Microcopy",
|
| 175 |
+
"External Ad",
|
| 176 |
+
"FAQs",
|
| 177 |
+
"Fear of Missing Out",
|
| 178 |
+
"Features",
|
| 179 |
+
"Features - Additional",
|
| 180 |
+
"Features - Bento-Style",
|
| 181 |
+
"Features - Z-Style",
|
| 182 |
+
"Features Accordion",
|
| 183 |
+
"Features Grid",
|
| 184 |
+
"Filters",
|
| 185 |
+
"Filters - Change Options Order",
|
| 186 |
+
"Filters - Emphasize",
|
| 187 |
+
"Filters - Options",
|
| 188 |
+
"Filters - Redesign",
|
| 189 |
+
"Filters - Show More Options",
|
| 190 |
+
"Flourishes / Small Design Details",
|
| 191 |
+
"Font Size",
|
| 192 |
+
"Footer Navigation",
|
| 193 |
+
"Form - Add",
|
| 194 |
+
"Form Activation",
|
| 195 |
+
"Form Center-Aligned",
|
| 196 |
+
"Form Color Change",
|
| 197 |
+
"Form Field Border Change",
|
| 198 |
+
"Form Field Label",
|
| 199 |
+
"Form Help",
|
| 200 |
+
"Form Over Partner Logos",
|
| 201 |
+
"Form Over Pricing",
|
| 202 |
+
"Form Over Resource",
|
| 203 |
+
"Form Over UI",
|
| 204 |
+
"Form Over UI - Animated",
|
| 205 |
+
"Form Over UI - Other",
|
| 206 |
+
"Form Over UI With Copy",
|
| 207 |
+
"Form Over UI With Integrations",
|
| 208 |
+
"Form Over UI With Reviews Summary",
|
| 209 |
+
"Form Over UI With Social Proof",
|
| 210 |
+
"Form Over UI With Testimonial",
|
| 211 |
+
"Form Over UI in Hero",
|
| 212 |
+
"Form Positioning",
|
| 213 |
+
"Form Redesign",
|
| 214 |
+
"Form in Modal",
|
| 215 |
+
"Form on the Left",
|
| 216 |
+
"Free Shipping",
|
| 217 |
+
"Free Shipping - Emphasize",
|
| 218 |
+
"Full-width Modal",
|
| 219 |
+
"G2/Forrester/Gartner Alignment Chart",
|
| 220 |
+
"GDPR Cookie Modal",
|
| 221 |
+
"GDPR Cookie Modal - Change",
|
| 222 |
+
"Gender Segmentation",
|
| 223 |
+
"Geo-specific Personalization",
|
| 224 |
+
"Guarantee",
|
| 225 |
+
"Hamburger Button",
|
| 226 |
+
"Headline",
|
| 227 |
+
"Headline Center-Aligned",
|
| 228 |
+
"Headline Copy Change",
|
| 229 |
+
"Hero Bar Navigation",
|
| 230 |
+
"Hero Center-Aligned",
|
| 231 |
+
"Hero Layout",
|
| 232 |
+
"Hero Redesign",
|
| 233 |
+
"Hero Size",
|
| 234 |
+
"Hero Tiles",
|
| 235 |
+
"Hide Price in Form",
|
| 236 |
+
"Highlight Words",
|
| 237 |
+
"How It Works",
|
| 238 |
+
"How It Works - Emphasize",
|
| 239 |
+
"Hyperlink",
|
| 240 |
+
"Icon Label",
|
| 241 |
+
"Icons",
|
| 242 |
+
"Image",
|
| 243 |
+
"Image/Video to Background",
|
| 244 |
+
"Impact Chart",
|
| 245 |
+
"Inclusions",
|
| 246 |
+
"Inclusions - Additional",
|
| 247 |
+
"Increase Number of Related Articles/Case Studies",
|
| 248 |
+
"Increase Trust",
|
| 249 |
+
"Industry Types",
|
| 250 |
+
"Infinite Scroll",
|
| 251 |
+
"Inline Form Field Copy",
|
| 252 |
+
"Inline Form Field Copy Change",
|
| 253 |
+
"Inline Link Nudge",
|
| 254 |
+
"Inline Validation",
|
| 255 |
+
"Insertion",
|
| 256 |
+
"Insertion Redesign",
|
| 257 |
+
"Instant Copy",
|
| 258 |
+
"Integrations",
|
| 259 |
+
"Interactive Hero",
|
| 260 |
+
"Interactive Modal",
|
| 261 |
+
"Interactive Product Section",
|
| 262 |
+
"Interactive Tour",
|
| 263 |
+
"Interest Rates",
|
| 264 |
+
"Interface in Background",
|
| 265 |
+
"Left Form Over UI",
|
| 266 |
+
"Link to External Sites",
|
| 267 |
+
"Link to Internal Page",
|
| 268 |
+
"Link to Product Listing",
|
| 269 |
+
"Live Chat",
|
| 270 |
+
"Live Trends",
|
| 271 |
+
"Local Languages",
|
| 272 |
+
"Localization Option",
|
| 273 |
+
"Locked Hero",
|
| 274 |
+
"Login Method",
|
| 275 |
+
"Login to Signup",
|
| 276 |
+
"Logo",
|
| 277 |
+
"Logo Positioning",
|
| 278 |
+
"Logo Size",
|
| 279 |
+
"Long Page",
|
| 280 |
+
"Longform Baseline",
|
| 281 |
+
"Map",
|
| 282 |
+
"Media Mentions Social Proof",
|
| 283 |
+
"Meet the People",
|
| 284 |
+
"Menu",
|
| 285 |
+
"Mini Case Studies",
|
| 286 |
+
"Mini Donation",
|
| 287 |
+
"Mini Reviews",
|
| 288 |
+
"Mini Triage in Nav",
|
| 289 |
+
"Minimize Price Font Size",
|
| 290 |
+
"Mixed Media",
|
| 291 |
+
"Modal",
|
| 292 |
+
"Modify Breadcrumbs",
|
| 293 |
+
"Modify Search Bar",
|
| 294 |
+
"More Detailed Reviews",
|
| 295 |
+
"More Expensive First",
|
| 296 |
+
"More Product Info",
|
| 297 |
+
"Move Bestsellers/Featured Products Up",
|
| 298 |
+
"Move Image Up",
|
| 299 |
+
"Multi-step Forms",
|
| 300 |
+
"Multiple Hero Images",
|
| 301 |
+
"Natural Language Forms",
|
| 302 |
+
"Navigation Bar",
|
| 303 |
+
"Navigation Bar Redesign",
|
| 304 |
+
"New Edition",
|
| 305 |
+
"Number of Purchases",
|
| 306 |
+
"Number of Slots / Stocks Progress Bar",
|
| 307 |
+
"Online / Active Now Status",
|
| 308 |
+
"Open in a New Tab",
|
| 309 |
+
"Optimized Copy",
|
| 310 |
+
"Optimized Copy - Form",
|
| 311 |
+
"Optimized Copy - Hero",
|
| 312 |
+
"Other",
|
| 313 |
+
"Out of Stock Products",
|
| 314 |
+
"Page Layout",
|
| 315 |
+
"Pain Point",
|
| 316 |
+
"Partner Logos",
|
| 317 |
+
"Payment First",
|
| 318 |
+
"Payment Options",
|
| 319 |
+
"People Like Me With Problems Like Mine",
|
| 320 |
+
"Personal Headline",
|
| 321 |
+
"Personalized OS",
|
| 322 |
+
"Postcode / Domain / Other Plus CTA",
|
| 323 |
+
"Pre-expanded Chatbot",
|
| 324 |
+
"Pre-expanded Dropdown",
|
| 325 |
+
"Pre-expanded Tooltip",
|
| 326 |
+
"Pre-expanded/Condensed FAQs",
|
| 327 |
+
"Pre-filled Text Box",
|
| 328 |
+
"Pre-selected Options",
|
| 329 |
+
"Price Per Month",
|
| 330 |
+
"Price Per Unit",
|
| 331 |
+
"Pricing Cards",
|
| 332 |
+
"Pricing Cards & Features Combined",
|
| 333 |
+
"Pricing Cards - Change / Add Emphasized Plan",
|
| 334 |
+
"Pricing Cards - Change Inclusions",
|
| 335 |
+
"Pricing Cards - Emphasize Inclusions",
|
| 336 |
+
"Pricing Cards - Inclusions",
|
| 337 |
+
"Pricing Defaulted to Annual",
|
| 338 |
+
"Pricing Defaulted to Subscription",
|
| 339 |
+
"Pricing Toggle",
|
| 340 |
+
"Product Comparison",
|
| 341 |
+
"Product Customization",
|
| 342 |
+
"Product Image",
|
| 343 |
+
"Product Organization",
|
| 344 |
+
"Product Subscription",
|
| 345 |
+
"Product/Bundle",
|
| 346 |
+
"Progress Bar",
|
| 347 |
+
"Progress Timeline",
|
| 348 |
+
"Promotion",
|
| 349 |
+
"Promotion - Discount Offer Banner",
|
| 350 |
+
"Promotion - Emphasize",
|
| 351 |
+
"Promotions - Trial Offer",
|
| 352 |
+
"Pros and Cons",
|
| 353 |
+
"Prozac",
|
| 354 |
+
"Prozac - Others",
|
| 355 |
+
"Prozac Copy Change",
|
| 356 |
+
"Purchase History",
|
| 357 |
+
"Push Copy Up",
|
| 358 |
+
"Push Form Up",
|
| 359 |
+
"Push Form Up - Hero",
|
| 360 |
+
"Push Guarantee Up",
|
| 361 |
+
"Push Pricing Up",
|
| 362 |
+
"QR Code",
|
| 363 |
+
"Qualifying Questions",
|
| 364 |
+
"Quantitative Headline",
|
| 365 |
+
"Quantity Selector",
|
| 366 |
+
"Quote Slider",
|
| 367 |
+
"Radical Redesign",
|
| 368 |
+
"Radical Redesign - Basecamp Pricing",
|
| 369 |
+
"Real People",
|
| 370 |
+
"Rearranged Form Content",
|
| 371 |
+
"Recently Viewed / Purchased",
|
| 372 |
+
"Recommended (Non-Product)",
|
| 373 |
+
"Recommended Tag",
|
| 374 |
+
"Redesign Footer",
|
| 375 |
+
"Redesign Modal",
|
| 376 |
+
"Redesign Review Ribbon",
|
| 377 |
+
"Redesign Section",
|
| 378 |
+
"Redesign Triage",
|
| 379 |
+
"Reduce Choices",
|
| 380 |
+
"Reduce Distractors",
|
| 381 |
+
"Reduce Form Length / Remove Form Fields",
|
| 382 |
+
"Reduce Lower Interest Content",
|
| 383 |
+
"Reduce Steps",
|
| 384 |
+
"Reduced Spacing",
|
| 385 |
+
"Referral Section",
|
| 386 |
+
"Related Articles",
|
| 387 |
+
"Related Products",
|
| 388 |
+
"Remove Advertisements / Ads",
|
| 389 |
+
"Remove Detail",
|
| 390 |
+
"Remove Escapes",
|
| 391 |
+
"Remove Hero",
|
| 392 |
+
"Remove Overages",
|
| 393 |
+
"Remove Section",
|
| 394 |
+
"Remove Slider",
|
| 395 |
+
"Remove Terms & Conditions",
|
| 396 |
+
"Remove Top Banner",
|
| 397 |
+
"Remove Video",
|
| 398 |
+
"Replace Animation With Video",
|
| 399 |
+
"Replace Icon With Image",
|
| 400 |
+
"Replace Image With Animation",
|
| 401 |
+
"Replace Image With Interface",
|
| 402 |
+
"Replace Image with Video",
|
| 403 |
+
"Resource Section",
|
| 404 |
+
"Returning Users",
|
| 405 |
+
"Review Ribbon",
|
| 406 |
+
"Reviewer Name",
|
| 407 |
+
"Reviews Section",
|
| 408 |
+
"Reviews Summary",
|
| 409 |
+
"Reviews Summary Positioning",
|
| 410 |
+
"Rewording",
|
| 411 |
+
"Savings",
|
| 412 |
+
"Savings - Emphasize",
|
| 413 |
+
"Scarcity",
|
| 414 |
+
"Scroll Down Animation",
|
| 415 |
+
"Search Bar / Button",
|
| 416 |
+
"Search Section",
|
| 417 |
+
"Search to Dropdown",
|
| 418 |
+
"Section Banner",
|
| 419 |
+
"Shipping Information",
|
| 420 |
+
"Shorten FAQs",
|
| 421 |
+
"Shorten Product Comparison",
|
| 422 |
+
"Shortened Forms",
|
| 423 |
+
"Shortform Baseline",
|
| 424 |
+
"Show / Change to Required / Mandatory Field",
|
| 425 |
+
"Show Annual Price",
|
| 426 |
+
"Show Daily Price",
|
| 427 |
+
"Show Interface",
|
| 428 |
+
"Show Price",
|
| 429 |
+
"Show Starting Price",
|
| 430 |
+
"Show Weekly Price",
|
| 431 |
+
"Side Navigation",
|
| 432 |
+
"Signup / Registration Wall",
|
| 433 |
+
"Signup to Demo",
|
| 434 |
+
"Simplify Design",
|
| 435 |
+
"Single Annual-Monthly Toggle in Pricing",
|
| 436 |
+
"Single CTA in Pricing",
|
| 437 |
+
"Single Select to Multiple Select Option / Choice",
|
| 438 |
+
"Single Sign-On (SSO)",
|
| 439 |
+
"Slider Positioning",
|
| 440 |
+
"Slider Thumbnail",
|
| 441 |
+
"Sliding Form",
|
| 442 |
+
"Slight Icon Change",
|
| 443 |
+
"Social Count",
|
| 444 |
+
"Social Media Links",
|
| 445 |
+
"Social Proof",
|
| 446 |
+
"Social Proof Copy Change",
|
| 447 |
+
"Social Proof in Hero",
|
| 448 |
+
"Specific Benefit*",
|
| 449 |
+
"Specific Contact",
|
| 450 |
+
"Specific Guarantee",
|
| 451 |
+
"Specific Headline",
|
| 452 |
+
"Split Screen",
|
| 453 |
+
"Split Screen with Sticky Form",
|
| 454 |
+
"Statistics",
|
| 455 |
+
"Statistics - Emphasize",
|
| 456 |
+
"Step Numbers",
|
| 457 |
+
"Sticky - Other",
|
| 458 |
+
"Sticky Banner",
|
| 459 |
+
"Sticky CTA",
|
| 460 |
+
"Sticky Footer",
|
| 461 |
+
"Sticky Form",
|
| 462 |
+
"Sticky Navigation Bar",
|
| 463 |
+
"Sticky Positioning",
|
| 464 |
+
"Sticky Redesign",
|
| 465 |
+
"Sticky Reviews",
|
| 466 |
+
"Sticky Search Bar",
|
| 467 |
+
"Sticky Side Navigation",
|
| 468 |
+
"Sticky Subnav",
|
| 469 |
+
"Store/Service Locations",
|
| 470 |
+
"Stories-style Video",
|
| 471 |
+
"Strikethrough Pricing",
|
| 472 |
+
"Subhead",
|
| 473 |
+
"Subhead Copy Change",
|
| 474 |
+
"Subscription/Plan Time/Duration",
|
| 475 |
+
"Superhead",
|
| 476 |
+
"Superhead Copy Change",
|
| 477 |
+
"Suppress Promo",
|
| 478 |
+
"Suppressed Content",
|
| 479 |
+
"Suppressed Copy",
|
| 480 |
+
"Table of Contents",
|
| 481 |
+
"Tags",
|
| 482 |
+
"Tease Pricing",
|
| 483 |
+
"Tease Savings",
|
| 484 |
+
"Tease Video / Content",
|
| 485 |
+
"Terms of Service",
|
| 486 |
+
"Testimonial Positioning",
|
| 487 |
+
"Testimonials",
|
| 488 |
+
"Testimonials With Stars",
|
| 489 |
+
"Text Field/Dropdown to Button",
|
| 490 |
+
"Text Field/Dropdown to Slider",
|
| 491 |
+
"Text on the Left",
|
| 492 |
+
"The Science / Method Behind",
|
| 493 |
+
"Time to Completion*",
|
| 494 |
+
"Time to First Ah-ha / Completion",
|
| 495 |
+
"Tooltip",
|
| 496 |
+
"Tooltip - Change",
|
| 497 |
+
"Tooltip Positioning",
|
| 498 |
+
"Top Banner Color Change",
|
| 499 |
+
"Top Banner Copy Change",
|
| 500 |
+
"Triage",
|
| 501 |
+
"Trust Badges",
|
| 502 |
+
"Trust Badges - Emphasize",
|
| 503 |
+
"Trust Badges in Hero",
|
| 504 |
+
"USPs - Logos / Icons / Images",
|
| 505 |
+
"Unique Selling Points (USPs)*",
|
| 506 |
+
"Upsell Membership",
|
| 507 |
+
"Upsell to Demo/Trial",
|
| 508 |
+
"Urgency",
|
| 509 |
+
"Use Case",
|
| 510 |
+
"Video in Hero",
|
| 511 |
+
"Wall of Testimonials",
|
| 512 |
+
"Warranty",
|
| 513 |
+
"Widen Webpage",
|
| 514 |
+
];
|
| 515 |
+
|
| 516 |
+
const industries = [
|
| 517 |
+
"Automotive & Transportation",
|
| 518 |
+
"B2B Services",
|
| 519 |
+
"B2B Software & Tech",
|
| 520 |
+
"Consumer Services",
|
| 521 |
+
"Consumer Software & Apps",
|
| 522 |
+
"Education",
|
| 523 |
+
"Finance, Insurance & Real Estate",
|
| 524 |
+
"Food, Hospitality & Travel",
|
| 525 |
+
"Health & Wellness",
|
| 526 |
+
"Industrial & Manufacturing",
|
| 527 |
+
"Media & Entertainment",
|
| 528 |
+
"Non-Profit & Government",
|
| 529 |
+
"Other",
|
| 530 |
+
"Retail & E-commerce"
|
| 531 |
+
];
|
| 532 |
+
|
| 533 |
+
const customerTypes = [
|
| 534 |
+
"B2B",
|
| 535 |
+
"B2C",
|
| 536 |
+
"Both",
|
| 537 |
+
"Other*"
|
| 538 |
+
];
|
| 539 |
+
|
| 540 |
+
const pageTypes = [
|
| 541 |
+
"Awareness & Discovery",
|
| 542 |
+
"Consideration & Evaluation",
|
| 543 |
+
"Conversion",
|
| 544 |
+
"Internal & Navigation",
|
| 545 |
+
"Post-Conversion & Other"
|
| 546 |
+
];
|
| 547 |
+
|
| 548 |
+
const results = [
|
| 549 |
+
"Loser",
|
| 550 |
+
"Neutral",
|
| 551 |
+
"Winner"
|
| 552 |
+
];
|
| 553 |
+
|
| 554 |
+
const businessModels = [
|
| 555 |
+
"E-Commerce",
|
| 556 |
+
"Lead Generation",
|
| 557 |
+
"Other*",
|
| 558 |
+
"SaaS"
|
| 559 |
+
];
|
| 560 |
+
|
| 561 |
+
const conversionTypes = [
|
| 562 |
+
"Direct Purchase",
|
| 563 |
+
"High-Intent Lead Gen",
|
| 564 |
+
"Info/Content Lead Gen",
|
| 565 |
+
"Location Search",
|
| 566 |
+
"Non-Profit/Community",
|
| 567 |
+
"Other Conversion"
|
| 568 |
+
];
|
| 569 |
+
|
| 570 |
+
// Create category mappings in the format expected by app.py
|
| 571 |
+
const categoryMappings = {
|
| 572 |
+
"Business Model": {
|
| 573 |
+
"num_categories": businessModels.length,
|
| 574 |
+
"categories": businessModels
|
| 575 |
+
},
|
| 576 |
+
"Customer Type": {
|
| 577 |
+
"num_categories": customerTypes.length,
|
| 578 |
+
"categories": customerTypes
|
| 579 |
+
},
|
| 580 |
+
"grouped_conversion_type": {
|
| 581 |
+
"num_categories": conversionTypes.length,
|
| 582 |
+
"categories": conversionTypes
|
| 583 |
+
},
|
| 584 |
+
"grouped_industry": {
|
| 585 |
+
"num_categories": industries.length,
|
| 586 |
+
"categories": industries
|
| 587 |
+
},
|
| 588 |
+
"grouped_page_type": {
|
| 589 |
+
"num_categories": pageTypes.length,
|
| 590 |
+
"categories": pageTypes
|
| 591 |
+
}
|
| 592 |
+
};
|
| 593 |
+
|
| 594 |
+
// Export the arrays and mappings so they can be imported into other files.
|
| 595 |
+
module.exports = {
|
| 596 |
+
industries,
|
| 597 |
+
customerTypes,
|
| 598 |
+
pageTypes,
|
| 599 |
+
results,
|
| 600 |
+
businessModels,
|
| 601 |
+
conversionTypes,
|
| 602 |
+
pattern,
|
| 603 |
+
categoryMappings,
|
| 604 |
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Business Model": {
|
| 3 |
+
"num_categories": 4,
|
| 4 |
+
"categories": [
|
| 5 |
+
"E-Commerce",
|
| 6 |
+
"Lead Generation",
|
| 7 |
+
"Other*",
|
| 8 |
+
"SaaS"
|
| 9 |
+
]
|
| 10 |
+
},
|
| 11 |
+
"Customer Type": {
|
| 12 |
+
"num_categories": 4,
|
| 13 |
+
"categories": [
|
| 14 |
+
"B2B",
|
| 15 |
+
"B2C",
|
| 16 |
+
"Both",
|
| 17 |
+
"Other*"
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
"grouped_conversion_type": {
|
| 21 |
+
"num_categories": 6,
|
| 22 |
+
"categories": [
|
| 23 |
+
"Direct Purchase",
|
| 24 |
+
"High-Intent Lead Gen",
|
| 25 |
+
"Info/Content Lead Gen",
|
| 26 |
+
"Location Search",
|
| 27 |
+
"Non-Profit/Community",
|
| 28 |
+
"Other Conversion"
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
"grouped_industry": {
|
| 32 |
+
"num_categories": 14,
|
| 33 |
+
"categories": [
|
| 34 |
+
"Automotive & Transportation",
|
| 35 |
+
"B2B Services",
|
| 36 |
+
"B2B Software & Tech",
|
| 37 |
+
"Consumer Services",
|
| 38 |
+
"Consumer Software & Apps",
|
| 39 |
+
"Education",
|
| 40 |
+
"Finance, Insurance & Real Estate",
|
| 41 |
+
"Food, Hospitality & Travel",
|
| 42 |
+
"Health & Wellness",
|
| 43 |
+
"Industrial & Manufacturing",
|
| 44 |
+
"Media & Entertainment",
|
| 45 |
+
"Non-Profit & Government",
|
| 46 |
+
"Other",
|
| 47 |
+
"Retail & E-commerce"
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
"grouped_page_type": {
|
| 51 |
+
"num_categories": 5,
|
| 52 |
+
"categories": [
|
| 53 |
+
"Awareness & Discovery",
|
| 54 |
+
"Consideration & Evaluation",
|
| 55 |
+
"Conversion",
|
| 56 |
+
"Internal & Navigation",
|
| 57 |
+
"Post-Conversion & Other"
|
| 58 |
+
]
|
| 59 |
+
}
|
| 60 |
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
tesseract-ocr
|
|
The diff for this file is too large to render.
See raw diff
|
|
|
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
torch
|
| 3 |
+
transformers
|
| 4 |
+
pandas
|
| 5 |
+
scikit-learn
|
| 6 |
+
Pillow
|
| 7 |
+
gradio
|
| 8 |
+
pytesseract
|
| 9 |
+
spaces
|
| 10 |
+
requests
|
| 11 |
+
huggingface_hub
|
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Setup Instructions for New ABTestPredictor Repository
|
| 2 |
+
|
| 3 |
+
## Files to Upload to Your New Hugging Face Space
|
| 4 |
+
|
| 5 |
+
### 1. Core Application Files
|
| 6 |
+
- `app.py` - Main application with dual-AI integration
|
| 7 |
+
- `requirements.txt` - Python dependencies
|
| 8 |
+
- `packages.txt` - System packages
|
| 9 |
+
- `README.md` - Documentation
|
| 10 |
+
|
| 11 |
+
### 2. Data Files
|
| 12 |
+
- `metadata.js` - Category definitions and mappings
|
| 13 |
+
- `confidence_scores.js` - Confidence scores for Industry + Page Type combinations
|
| 14 |
+
- `patterbs.json` - Pattern descriptions for Gemini Pro analysis
|
| 15 |
+
|
| 16 |
+
### 3. Model Files
|
| 17 |
+
- `model/multimodal_cat_mappings_GGG.json` - Category mappings for GGG model
|
| 18 |
+
- Upload `multimodal_gated_model_2.7_GGG.pth` directly via Hugging Face Files tab
|
| 19 |
+
|
| 20 |
+
## 🔑 Required API Keys (Set in Spaces Settings)
|
| 21 |
+
|
| 22 |
+
### Secrets to Add:
|
| 23 |
+
1. **Name**: `PERPLEXITY_API_KEY`
|
| 24 |
+
**Value**: Your Perplexity API key (starts with `pplx-`)
|
| 25 |
+
|
| 26 |
+
2. **Name**: `GEMINI_API_KEY`
|
| 27 |
+
**Value**: Your Google Gemini API key
|
| 28 |
+
|
| 29 |
+
## 🚀 Upload Process
|
| 30 |
+
|
| 31 |
+
### Option 1: Manual Upload
|
| 32 |
+
1. Go to your new Hugging Face Space
|
| 33 |
+
2. Upload all files via the web interface
|
| 34 |
+
3. Set the API keys in Settings → Variables and secrets
|
| 35 |
+
|
| 36 |
+
### Option 2: Git Upload
|
| 37 |
+
1. Clone your new repository: `git clone https://huggingface.co/spaces/nitish-spz/ABTestPredictor`
|
| 38 |
+
2. Copy all files from this directory to the cloned directory
|
| 39 |
+
3. Commit and push: `git add . && git commit -m "Complete app setup" && git push`
|
| 40 |
+
|
| 41 |
+
## ✅ Verification
|
| 42 |
+
|
| 43 |
+
After upload, your space should show:
|
| 44 |
+
- ✅ Dual-AI powered analysis tabs
|
| 45 |
+
- ✅ Enhanced model architecture loaded
|
| 46 |
+
- ✅ 359 pattern detection capabilities
|
| 47 |
+
- ✅ Confidence scoring with training statistics
|
| 48 |
+
|
| 49 |
+
## 🎯 Features Ready
|
| 50 |
+
|
| 51 |
+
- **Smart Auto-Prediction**: AI categorization + pattern detection
|
| 52 |
+
- **Manual Selection**: Traditional dropdown interface
|
| 53 |
+
- **Batch Prediction**: CSV file processing
|
| 54 |
+
- **Enhanced Results**: Comprehensive analysis with confidence metrics
|
| 55 |
+
|
| 56 |
+
Your enhanced A/B test predictor with dual-AI analysis is ready to deploy! 🎉
|