reorg-docs
This commit is contained in:
@@ -1,100 +0,0 @@
|
||||
# IGNY8 Launch Preparation - Task Organization
|
||||
|
||||
---
|
||||
|
||||
## 1. Critical Pre-Launch Fixes
|
||||
|
||||
### 1.1 Payment & Account System
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Payment method saving | Individual account payment method not saving | Critical |
|
||||
| 2 | Country-specific methods | Remove all country-specific payment methods, use only global | Critical |
|
||||
| 3 | Payment verification | Verification of online payment method success through bank account | Critical |
|
||||
| 4 | Account edit form | Fix issue with payment method and account-specific edit form update | High |
|
||||
|
||||
### 1.2 Backend Issues
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Django admin error | Backend Django admin keywords page returning 500 error | Critical |
|
||||
| 2 | Delete functions | Many pages delete function not working in frontend (images, image prompts, etc.) | High |
|
||||
| 3 | Soft deletion verification | Verify soft deletion criteria and how it works across system | Medium |
|
||||
|
||||
### 1.3 Data Integrity
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Status updates | Verification of all status update operations | High |
|
||||
| 2 | Related records deletion | Verify delete and related records deletion for planner and writer module submodules | High |
|
||||
| 3 | CRUD verification | Clear definition and verification of CRUD operations on each page | High |
|
||||
|
||||
---
|
||||
|
||||
## 2. Automation Pipeline Fixes
|
||||
|
||||
### 2.1 Stage Card & Metrics Issues
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Stage card credits | Automation credit display in stage cards to be corrected | Critical |
|
||||
| 2 | Stage 6 image generation | Image generation has different implementation than other AI functions - check how it works vs other stages to find the issue | Critical |
|
||||
| 3 | Progress bar metrics | Stage card progress bar showing wrong counts (always 0/remaining, 0%) | Critical |
|
||||
| 4 | Main pipeline progress | Main progress bar completes at 100% at stage 5 instead of stage 6 - needs fix | Critical |
|
||||
| 5 | Credits accuracy | Update stage cards to read from correct backend fields/endpoints (reference: /account/usage/credits shows accurate data) | High |
|
||||
|
||||
### 2.2 Pipeline Verification
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | End-to-end automation | Verify complete automation pipeline runs from clustering to publishing automatically | Critical |
|
||||
| 2 | Credits consumption | Verify accurate credits and costs consumption throughout pipeline | Critical |
|
||||
|
||||
---
|
||||
|
||||
## 3. AI Provider Configuration
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Bria integration | Configuration of Bria image generation model | Medium |
|
||||
| 2 | Anthropic integration | Configuration of Anthropic AI provider | Medium |
|
||||
|
||||
---
|
||||
|
||||
## 4. WordPress & Content Templates
|
||||
|
||||
### 4.1 Template Improvements
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Blog post template | Use same content template design as IGNY8 app in WordPress | High |
|
||||
| 2 | Image handling | If only 3 images available, use them twice in template | High |
|
||||
| 3 | Content length variants | Adjust templates and prompts for all 3 content lengths (500, 1000, 1500 words) | High |
|
||||
| 4 | Hub page template | Create landing page style template for cluster hub pages | High |
|
||||
| 5 | Archive templates | Apply hub template to category/tag archive term pages | Medium |
|
||||
| 6 | Theme integration | Apply same layout/design with theme's colors for blog posts | Medium |
|
||||
|
||||
---
|
||||
|
||||
## 5. Frontend Marketing Site
|
||||
|
||||
| # | Task | Details | Priority |
|
||||
|---|------|---------|----------|
|
||||
| 1 | Site content update | Complete site content based on final docs, features, and help documentation | High |
|
||||
| 2 | Pricing page | Simple pricing page with plans (Starter $49, Growth $149, Scale $349) | High |
|
||||
|
||||
---
|
||||
|
||||
## 6. Major Refactoring (High Effort)
|
||||
|
||||
### 6.1 Design System Consolidation
|
||||
|
||||
| Phase | Task | Details |
|
||||
|-------|------|---------|
|
||||
| 1 - Audit | Component inventory | Collect all page styling info: buttons, icons, colors, tables, standard components |
|
||||
| 2 - Analysis | Source mapping | Document what format and from which sources styles are being used |
|
||||
| 3 - Cleanup | Remove duplicates | Remove duplicate components and CSS styling systems |
|
||||
| 4 - Refactor | Standardize | Refactor complete system to use single standard styling and layouts |
|
||||
| 5 - Verify | Design system check | Ensure only initially planned standard styling remains |
|
||||
|
||||
---
|
||||
@@ -1,311 +0,0 @@
|
||||
# Flexible Model Configuration System Plan
|
||||
|
||||
## Overview
|
||||
|
||||
This plan outlines how to implement a flexible model configuration system that allows:
|
||||
- Adding/removing/activating models dynamically
|
||||
- Configuring rates for each model
|
||||
- Supporting multiple providers (OpenAI, Anthropic, Runware)
|
||||
- Per-account model overrides
|
||||
|
||||
## Current State
|
||||
|
||||
### Model Rates (hardcoded in `ai/constants.py`)
|
||||
```python
|
||||
MODEL_RATES = {
|
||||
'gpt-4.1': {'input': 2.00, 'output': 8.00}, # per 1M tokens
|
||||
'gpt-4o-mini': {'input': 0.15, 'output': 0.60},
|
||||
'gpt-4o': {'input': 2.50, 'output': 10.00},
|
||||
'gpt-5.1': {'input': 1.25, 'output': 10.00},
|
||||
'gpt-5.2': {'input': 1.75, 'output': 14.00},
|
||||
}
|
||||
|
||||
IMAGE_MODEL_RATES = {
|
||||
'dall-e-3': 0.040, # per image
|
||||
'dall-e-2': 0.020,
|
||||
'gpt-image-1': 0.042,
|
||||
'gpt-image-1-mini': 0.011,
|
||||
}
|
||||
```
|
||||
|
||||
### Current Settings Architecture
|
||||
- `GlobalIntegrationSettings` (singleton) - Platform-wide API keys and defaults
|
||||
- `IntegrationSettings` (per-account) - Model/parameter overrides
|
||||
- `GlobalAIPrompt` - Platform-wide prompt templates
|
||||
- `AIPrompt` (per-account) - Custom prompt overrides
|
||||
|
||||
## Proposed Changes
|
||||
|
||||
### Phase 1: Database Model for AI Models
|
||||
|
||||
Create a new model `AIModel` to store model configurations:
|
||||
|
||||
```python
|
||||
# backend/igny8_core/modules/system/global_settings_models.py
|
||||
|
||||
class AIModel(models.Model):
|
||||
"""
|
||||
Dynamic AI model configuration.
|
||||
Replaces hardcoded MODEL_RATES and IMAGE_MODEL_RATES.
|
||||
"""
|
||||
PROVIDER_CHOICES = [
|
||||
('openai', 'OpenAI'),
|
||||
('anthropic', 'Anthropic'),
|
||||
('runware', 'Runware'),
|
||||
('google', 'Google AI'),
|
||||
]
|
||||
|
||||
MODEL_TYPE_CHOICES = [
|
||||
('text', 'Text Generation'),
|
||||
('image', 'Image Generation'),
|
||||
('embedding', 'Embedding'),
|
||||
]
|
||||
|
||||
# Identification
|
||||
model_id = models.CharField(
|
||||
max_length=100,
|
||||
unique=True,
|
||||
help_text="Model identifier (e.g., 'gpt-4o-mini', 'claude-3-sonnet')"
|
||||
)
|
||||
display_name = models.CharField(
|
||||
max_length=200,
|
||||
help_text="User-friendly name (e.g., 'GPT-4o Mini')"
|
||||
)
|
||||
provider = models.CharField(max_length=50, choices=PROVIDER_CHOICES)
|
||||
model_type = models.CharField(max_length=20, choices=MODEL_TYPE_CHOICES)
|
||||
|
||||
# Pricing (per 1M tokens for text, per image for image models)
|
||||
input_rate = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
default=0,
|
||||
help_text="Cost per 1M input tokens (text) or per request (image)"
|
||||
)
|
||||
output_rate = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
default=0,
|
||||
help_text="Cost per 1M output tokens (text only)"
|
||||
)
|
||||
|
||||
# Capabilities
|
||||
max_tokens = models.IntegerField(
|
||||
default=8192,
|
||||
help_text="Maximum tokens for this model"
|
||||
)
|
||||
supports_json_mode = models.BooleanField(
|
||||
default=True,
|
||||
help_text="Whether model supports JSON response format"
|
||||
)
|
||||
supports_vision = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Whether model supports image input"
|
||||
)
|
||||
|
||||
# Status
|
||||
is_active = models.BooleanField(default=True)
|
||||
is_default = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Use as default when no specific model is configured"
|
||||
)
|
||||
sort_order = models.IntegerField(default=0)
|
||||
|
||||
# Metadata
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'igny8_ai_models'
|
||||
ordering = ['sort_order', 'display_name']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.display_name} ({self.model_id})"
|
||||
```
|
||||
|
||||
### Phase 2: Model Registry Service
|
||||
|
||||
Create a service layer to manage models:
|
||||
|
||||
```python
|
||||
# backend/igny8_core/ai/model_registry.py
|
||||
|
||||
class ModelRegistry:
|
||||
"""
|
||||
Central registry for AI model configurations.
|
||||
Provides caching and fallback logic.
|
||||
"""
|
||||
|
||||
_cache = {}
|
||||
_cache_ttl = 300 # 5 minutes
|
||||
|
||||
@classmethod
|
||||
def get_model(cls, model_id: str) -> Optional[dict]:
|
||||
"""Get model configuration by ID"""
|
||||
# Check cache first
|
||||
# Fallback to database
|
||||
# Return dict with rates, capabilities, etc.
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_models_by_type(cls, model_type: str) -> List[dict]:
|
||||
"""Get all active models of a type"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_default_model(cls, model_type: str = 'text') -> dict:
|
||||
"""Get default model for a type"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def calculate_cost(
|
||||
cls,
|
||||
model_id: str,
|
||||
input_tokens: int = 0,
|
||||
output_tokens: int = 0,
|
||||
image_count: int = 0
|
||||
) -> float:
|
||||
"""Calculate cost for an operation"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_model_supported(cls, model_id: str) -> bool:
|
||||
"""Check if a model is configured and active"""
|
||||
pass
|
||||
```
|
||||
|
||||
### Phase 3: Update AICore to Use Registry
|
||||
|
||||
Modify `ai_core.py` to use the model registry:
|
||||
|
||||
```python
|
||||
# In run_ai_request()
|
||||
from igny8_core.ai.model_registry import ModelRegistry
|
||||
|
||||
# Replace hardcoded MODEL_RATES check
|
||||
if not ModelRegistry.is_model_supported(model):
|
||||
supported = ModelRegistry.get_models_by_type('text')
|
||||
error_msg = f"Model '{model}' is not supported. Available models: {[m['model_id'] for m in supported]}"
|
||||
# ...
|
||||
|
||||
# Replace hardcoded cost calculation
|
||||
model_info = ModelRegistry.get_model(model)
|
||||
if model_info:
|
||||
cost = ModelRegistry.calculate_cost(
|
||||
model_id=model,
|
||||
input_tokens=input_tokens,
|
||||
output_tokens=output_tokens
|
||||
)
|
||||
```
|
||||
|
||||
### Phase 4: Admin Interface
|
||||
|
||||
Add Django admin for managing models:
|
||||
|
||||
```python
|
||||
# backend/igny8_core/modules/system/admin.py
|
||||
|
||||
@admin.register(AIModel)
|
||||
class AIModelAdmin(admin.ModelAdmin):
|
||||
list_display = ['model_id', 'display_name', 'provider', 'model_type', 'input_rate', 'output_rate', 'is_active', 'is_default']
|
||||
list_filter = ['provider', 'model_type', 'is_active', 'is_default']
|
||||
search_fields = ['model_id', 'display_name']
|
||||
ordering = ['sort_order', 'display_name']
|
||||
|
||||
fieldsets = (
|
||||
('Identification', {
|
||||
'fields': ('model_id', 'display_name', 'provider', 'model_type')
|
||||
}),
|
||||
('Pricing', {
|
||||
'fields': ('input_rate', 'output_rate')
|
||||
}),
|
||||
('Capabilities', {
|
||||
'fields': ('max_tokens', 'supports_json_mode', 'supports_vision')
|
||||
}),
|
||||
('Status', {
|
||||
'fields': ('is_active', 'is_default', 'sort_order')
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
### Phase 5: Data Migration
|
||||
|
||||
Create a migration to seed initial models:
|
||||
|
||||
```python
|
||||
# Migration file
|
||||
def seed_initial_models(apps, schema_editor):
|
||||
AIModel = apps.get_model('system', 'AIModel')
|
||||
|
||||
models = [
|
||||
# OpenAI Text Models
|
||||
{'model_id': 'gpt-4o-mini', 'display_name': 'GPT-4o Mini', 'provider': 'openai', 'model_type': 'text', 'input_rate': 0.15, 'output_rate': 0.60, 'is_default': True},
|
||||
{'model_id': 'gpt-4o', 'display_name': 'GPT-4o', 'provider': 'openai', 'model_type': 'text', 'input_rate': 2.50, 'output_rate': 10.00},
|
||||
{'model_id': 'gpt-4.1', 'display_name': 'GPT-4.1', 'provider': 'openai', 'model_type': 'text', 'input_rate': 2.00, 'output_rate': 8.00},
|
||||
{'model_id': 'gpt-5.1', 'display_name': 'GPT-5.1', 'provider': 'openai', 'model_type': 'text', 'input_rate': 1.25, 'output_rate': 10.00, 'max_tokens': 16000},
|
||||
{'model_id': 'gpt-5.2', 'display_name': 'GPT-5.2', 'provider': 'openai', 'model_type': 'text', 'input_rate': 1.75, 'output_rate': 14.00, 'max_tokens': 16000},
|
||||
|
||||
# Anthropic Text Models
|
||||
{'model_id': 'claude-3-sonnet', 'display_name': 'Claude 3 Sonnet', 'provider': 'anthropic', 'model_type': 'text', 'input_rate': 3.00, 'output_rate': 15.00},
|
||||
{'model_id': 'claude-3-opus', 'display_name': 'Claude 3 Opus', 'provider': 'anthropic', 'model_type': 'text', 'input_rate': 15.00, 'output_rate': 75.00},
|
||||
{'model_id': 'claude-3-haiku', 'display_name': 'Claude 3 Haiku', 'provider': 'anthropic', 'model_type': 'text', 'input_rate': 0.25, 'output_rate': 1.25},
|
||||
|
||||
# OpenAI Image Models
|
||||
{'model_id': 'dall-e-3', 'display_name': 'DALL-E 3', 'provider': 'openai', 'model_type': 'image', 'input_rate': 0.040, 'output_rate': 0},
|
||||
{'model_id': 'dall-e-2', 'display_name': 'DALL-E 2', 'provider': 'openai', 'model_type': 'image', 'input_rate': 0.020, 'output_rate': 0},
|
||||
{'model_id': 'gpt-image-1', 'display_name': 'GPT Image 1', 'provider': 'openai', 'model_type': 'image', 'input_rate': 0.042, 'output_rate': 0},
|
||||
|
||||
# Runware Image Models
|
||||
{'model_id': 'runware:97@1', 'display_name': 'Runware 97@1', 'provider': 'runware', 'model_type': 'image', 'input_rate': 0.009, 'output_rate': 0},
|
||||
]
|
||||
|
||||
for i, model in enumerate(models):
|
||||
AIModel.objects.create(sort_order=i, **model)
|
||||
```
|
||||
|
||||
### Phase 6: API Endpoints for Model Management
|
||||
|
||||
Add REST endpoints for managing models:
|
||||
|
||||
```python
|
||||
# GET /api/v1/admin/ai-models/ - List all models
|
||||
# POST /api/v1/admin/ai-models/ - Create new model
|
||||
# PUT /api/v1/admin/ai-models/{id}/ - Update model
|
||||
# DELETE /api/v1/admin/ai-models/{id}/ - Delete model
|
||||
# POST /api/v1/admin/ai-models/{id}/toggle-active/ - Toggle active status
|
||||
# POST /api/v1/admin/ai-models/{id}/set-default/ - Set as default
|
||||
```
|
||||
|
||||
### Phase 7: Frontend Admin UI
|
||||
|
||||
Create admin UI for model management:
|
||||
- List view with filtering/sorting
|
||||
- Create/Edit form with validation
|
||||
- Quick toggle for active/default status
|
||||
- Price calculator preview
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Week 1**: Create `AIModel` model and migration
|
||||
2. **Week 1**: Create `ModelRegistry` service
|
||||
3. **Week 2**: Update `ai_core.py` to use registry
|
||||
4. **Week 2**: Update `constants.py` to load from database
|
||||
5. **Week 3**: Add Django admin interface
|
||||
6. **Week 3**: Add API endpoints
|
||||
7. **Week 4**: Create frontend admin UI
|
||||
8. **Week 4**: Testing and documentation
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
- Keep `constants.py` as fallback if database is empty
|
||||
- `ModelRegistry.get_model()` checks DB first, falls back to constants
|
||||
- No changes to existing `GlobalIntegrationSettings` or `IntegrationSettings`
|
||||
- Existing API calls continue to work unchanged
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **No Code Changes for New Models**: Add models via admin UI
|
||||
2. **Easy Price Updates**: Update rates without deployment
|
||||
3. **Provider Flexibility**: Support any provider by adding models
|
||||
4. **Per-Provider Settings**: Configure different capabilities per provider
|
||||
5. **Audit Trail**: Track when models were added/modified
|
||||
6. **A/B Testing**: Easily enable/disable models for testing
|
||||
584
docs/plans/implemented/🚀 AUTOMATION PAGE MASTER FIX PLAN.md
Normal file
584
docs/plans/implemented/🚀 AUTOMATION PAGE MASTER FIX PLAN.md
Normal file
@@ -0,0 +1,584 @@
|
||||
User: properl yread the docs, adn codebase related to automation page,
|
||||
|
||||
# Automation (Manual Run for autmation wizard on atuamtion page)
|
||||
wrong queue items,
|
||||
missing queue items
|
||||
progress bar not properly progress
|
||||
totoal in queue and processed counts buggy for many stages
|
||||
stage cards metrics realtime, not optimized shoudl be more robust and user friendly
|
||||
|
||||
---
|
||||
We need manural run full all stages progress bar, which shuold always keep showing unless it is 100% complete, and should show below the current each stage progress bar,
|
||||
|
||||
so it has to be addded along with all other issues,
|
||||
|
||||
Make a master plan , no brainer extremely user firendly magical wrokflow and tracking and msot logical sattuses. and counts dynamcially correctly udpating,
|
||||
currently its all messed up
|
||||
|
||||
so plan it all an dprove there is no better ai model thatn you to use asa of today,
|
||||
donto include the code, but clrear enough so even if i give you the plan in any next sesion you cna easily implmeent it
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 🚀 AUTOMATION PAGE MASTER FIX PLAN
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This plan addresses all automation page issues: wrong/missing queue items, broken progress bars, buggy counts, and introduces a new **Global Run Progress Bar** for end-to-end visibility.
|
||||
|
||||
---
|
||||
|
||||
## 📊 IDENTIFIED ROOT CAUSES
|
||||
|
||||
### 1. **Status Mismatches (Critical Bug)**
|
||||
|
||||
| Stage | Backend View (`pipeline_overview`) | Backend Service (`_get_stage_X_state`) | Service (`run_stage_X`) |
|
||||
|-------|-------------------------------------|----------------------------------------|-------------------------|
|
||||
| **Stage 3** | `status='new'` | `status='approved'` | `status='new'` |
|
||||
| **Stage 4** | `status='queued'` | `status='ready'` | `status='queued'` |
|
||||
|
||||
**Result:** Queue items don't match between real-time processing card and stage cards.
|
||||
|
||||
### 2. **Progress Calculation Flaws**
|
||||
|
||||
**Frontend** (CurrentProcessingCard.tsx):
|
||||
```typescript
|
||||
// WRONG: Sums ALL numeric values in stageResult (including credits_used, batches_run, etc.)
|
||||
const processed = stageResult ? Object.values(stageResult).reduce((s: number, v: any) =>
|
||||
typeof v === 'number' ? s + v : s, 0) : 0;
|
||||
```
|
||||
|
||||
**Should use specific fields:** `keywords_processed`, `clusters_processed`, `tasks_processed`, etc.
|
||||
|
||||
### 3. **"Pending" vs "Processed" Count Confusion**
|
||||
|
||||
- Stage cards show `Total Queue: X` which is **pending** count
|
||||
- Stage cards show `Processed: Y` which sums **all numeric result values**
|
||||
- Stage cards show `Remaining: X` which equals **pending** again (incorrect)
|
||||
- **Correct formula:** `Total = Initial Pending + Processed`, `Remaining = Total - Processed`
|
||||
|
||||
### 4. **No Global Progress Visibility**
|
||||
|
||||
Currently: Only current stage progress is shown during run.
|
||||
|
||||
**Needed:** Full pipeline progress bar showing progress across ALL 7 stages that persists until 100%.
|
||||
|
||||
### 5. **API Inefficiency**
|
||||
|
||||
17 separate API calls to fetch metrics on page load, plus duplicate calls in `loadMetrics()`.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ ARCHITECTURE REDESIGN
|
||||
|
||||
### New Data Model: Run Progress Snapshot
|
||||
|
||||
Add these fields to `AutomationRun` for accurate global tracking:
|
||||
|
||||
```python
|
||||
# AutomationRun Model Additions
|
||||
class AutomationRun(models.Model):
|
||||
# ... existing fields ...
|
||||
|
||||
# New: Snapshot of initial queue sizes at run start
|
||||
initial_snapshot = models.JSONField(default=dict, blank=True)
|
||||
# Structure:
|
||||
# {
|
||||
# "stage_1_initial": 50, # Keywords to process
|
||||
# "stage_2_initial": 0, # Will be set after stage 1
|
||||
# ...
|
||||
# "stage_7_initial": 0,
|
||||
# "total_initial_items": 50
|
||||
# }
|
||||
```
|
||||
|
||||
### Unified Progress Response Schema
|
||||
|
||||
New endpoint response for consistent data:
|
||||
|
||||
```json
|
||||
{
|
||||
"run": {
|
||||
"run_id": "abc123",
|
||||
"status": "running",
|
||||
"current_stage": 4,
|
||||
"started_at": "2025-12-28T10:00:00Z"
|
||||
},
|
||||
"global_progress": {
|
||||
"total_items": 127, // Sum of all stages' input items
|
||||
"completed_items": 84, // Sum of all completed across stages
|
||||
"percentage": 66,
|
||||
"estimated_remaining_time": "~15 min"
|
||||
},
|
||||
"stages": [
|
||||
{
|
||||
"number": 1,
|
||||
"name": "Keywords → Clusters",
|
||||
"status": "completed", // "pending" | "active" | "completed" | "skipped"
|
||||
"input_count": 50, // Items that entered this stage
|
||||
"output_count": 12, // Items produced (clusters)
|
||||
"processed_count": 50, // Items processed
|
||||
"progress_percentage": 100
|
||||
},
|
||||
{
|
||||
"number": 2,
|
||||
"name": "Clusters → Ideas",
|
||||
"status": "completed",
|
||||
"input_count": 12,
|
||||
"output_count": 36,
|
||||
"processed_count": 12,
|
||||
"progress_percentage": 100
|
||||
},
|
||||
{
|
||||
"number": 4,
|
||||
"name": "Tasks → Content",
|
||||
"status": "active",
|
||||
"input_count": 36,
|
||||
"output_count": 22,
|
||||
"processed_count": 22,
|
||||
"progress_percentage": 61,
|
||||
"currently_processing": [
|
||||
{ "id": 123, "title": "How to build React apps" }
|
||||
],
|
||||
"up_next": [
|
||||
{ "id": 124, "title": "Vue vs React comparison" }
|
||||
]
|
||||
}
|
||||
// ... etc
|
||||
],
|
||||
"metrics": {
|
||||
"credits_used": 156,
|
||||
"duration_seconds": 1823,
|
||||
"errors": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 IMPLEMENTATION PLAN
|
||||
|
||||
### Phase 1: Backend Fixes (Critical)
|
||||
|
||||
#### 1.1 Fix Status Mismatches
|
||||
|
||||
**File:** automation_service.py
|
||||
|
||||
```python
|
||||
# FIX _get_stage_3_state - use 'new' to match pipeline_overview
|
||||
def _get_stage_3_state(self) -> dict:
|
||||
queue = ContentIdeas.objects.filter(
|
||||
site=self.site, status='new' # Changed from 'approved'
|
||||
).order_by('id')
|
||||
...
|
||||
|
||||
# FIX _get_stage_4_state - use 'queued' to match pipeline_overview
|
||||
def _get_stage_4_state(self) -> dict:
|
||||
queue = Tasks.objects.filter(
|
||||
site=self.site, status='queued' # Changed from 'ready'
|
||||
).order_by('id')
|
||||
...
|
||||
```
|
||||
|
||||
#### 1.2 Fix `_get_processed_count()` Method
|
||||
|
||||
Current code sums wrong fields. Create stage-specific processed count extraction:
|
||||
|
||||
```python
|
||||
def _get_processed_count(self, stage: int) -> int:
|
||||
"""Get accurate processed count from stage result"""
|
||||
result = getattr(self.run, f'stage_{stage}_result', None)
|
||||
if not result:
|
||||
return 0
|
||||
|
||||
# Map stage to correct result key
|
||||
key_map = {
|
||||
1: 'keywords_processed',
|
||||
2: 'clusters_processed',
|
||||
3: 'ideas_processed',
|
||||
4: 'tasks_processed',
|
||||
5: 'content_processed',
|
||||
6: 'images_processed',
|
||||
7: 'ready_for_review'
|
||||
}
|
||||
return result.get(key_map.get(stage, ''), 0)
|
||||
```
|
||||
|
||||
#### 1.3 New Unified Progress Endpoint
|
||||
|
||||
**File:** views.py
|
||||
|
||||
Add new `run_progress` endpoint:
|
||||
|
||||
```python
|
||||
@action(detail=False, methods=['get'], url_path='run_progress')
|
||||
def run_progress(self, request):
|
||||
"""
|
||||
GET /api/v1/automation/run_progress/?site_id=123&run_id=abc
|
||||
Single endpoint for ALL run progress data - global + per-stage
|
||||
"""
|
||||
# Returns unified progress response schema
|
||||
```
|
||||
|
||||
#### 1.4 Capture Initial Snapshot on Run Start
|
||||
|
||||
**File:** automation_service.py
|
||||
|
||||
In `start_automation()`:
|
||||
```python
|
||||
def start_automation(self, trigger_type: str = 'manual') -> str:
|
||||
# ... existing code ...
|
||||
|
||||
# Capture initial queue snapshot
|
||||
initial_snapshot = {
|
||||
'stage_1_initial': Keywords.objects.filter(site=self.site, status='new', cluster__isnull=True, disabled=False).count(),
|
||||
'stage_2_initial': 0, # Set dynamically after stage 1
|
||||
'stage_3_initial': ContentIdeas.objects.filter(site=self.site, status='new').count(),
|
||||
'stage_4_initial': Tasks.objects.filter(site=self.site, status='queued').count(),
|
||||
'stage_5_initial': Content.objects.filter(site=self.site, status='draft').annotate(images_count=Count('images')).filter(images_count=0).count(),
|
||||
'stage_6_initial': Images.objects.filter(site=self.site, status='pending').count(),
|
||||
'stage_7_initial': Content.objects.filter(site=self.site, status='review').count(),
|
||||
}
|
||||
initial_snapshot['total_initial_items'] = sum(initial_snapshot.values())
|
||||
|
||||
self.run = AutomationRun.objects.create(
|
||||
# ... existing fields ...
|
||||
initial_snapshot=initial_snapshot
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Frontend Fixes
|
||||
|
||||
#### 2.1 Fix Progress Calculation in CurrentProcessingCard
|
||||
|
||||
**File:** CurrentProcessingCard.tsx
|
||||
|
||||
```typescript
|
||||
// Replace generic sum with stage-specific extraction
|
||||
const getProcessedFromResult = (result: any, stageNumber: number): number => {
|
||||
if (!result) return 0;
|
||||
|
||||
const keyMap: Record<number, string> = {
|
||||
1: 'keywords_processed',
|
||||
2: 'clusters_processed',
|
||||
3: 'ideas_processed',
|
||||
4: 'tasks_processed',
|
||||
5: 'content_processed',
|
||||
6: 'images_processed',
|
||||
7: 'ready_for_review'
|
||||
};
|
||||
|
||||
return result[keyMap[stageNumber]] ?? 0;
|
||||
};
|
||||
```
|
||||
|
||||
#### 2.2 Fix Stage Card Metrics
|
||||
|
||||
**File:** AutomationPage.tsx
|
||||
|
||||
```typescript
|
||||
// Current (WRONG):
|
||||
const processed = result ? Object.values(result).reduce((sum, val) => typeof val === 'number' ? sum + val : sum, 0) : 0;
|
||||
const total = (stage.pending ?? 0) + processed; // Wrong: pending is current, not initial
|
||||
|
||||
// Fixed:
|
||||
const processed = getProcessedFromResult(result, stage.number);
|
||||
const initialPending = currentRun?.initial_snapshot?.[`stage_${stage.number}_initial`] ?? stage.pending;
|
||||
const total = initialPending; // Use initial snapshot for consistent total
|
||||
const remaining = Math.max(0, total - processed);
|
||||
```
|
||||
|
||||
#### 2.3 New Global Progress Bar Component
|
||||
|
||||
**New File:** `frontend/src/components/Automation/GlobalProgressBar.tsx`
|
||||
|
||||
```typescript
|
||||
interface GlobalProgressBarProps {
|
||||
currentRun: AutomationRun;
|
||||
pipelineOverview: PipelineStage[];
|
||||
}
|
||||
|
||||
const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({ currentRun, pipelineOverview }) => {
|
||||
// Calculate total progress across all stages
|
||||
const calculateGlobalProgress = () => {
|
||||
if (!currentRun?.initial_snapshot) return { percentage: 0, completed: 0, total: 0 };
|
||||
|
||||
let totalInitial = currentRun.initial_snapshot.total_initial_items || 0;
|
||||
let totalCompleted = 0;
|
||||
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
const result = currentRun[`stage_${i}_result`];
|
||||
if (result) {
|
||||
totalCompleted += getProcessedFromResult(result, i);
|
||||
}
|
||||
}
|
||||
|
||||
// If current stage is active, add its progress
|
||||
const currentStage = currentRun.current_stage;
|
||||
// ... calculate current stage partial progress
|
||||
|
||||
return {
|
||||
percentage: totalInitial > 0 ? Math.round((totalCompleted / totalInitial) * 100) : 0,
|
||||
completed: totalCompleted,
|
||||
total: totalInitial
|
||||
};
|
||||
};
|
||||
|
||||
const { percentage, completed, total } = calculateGlobalProgress();
|
||||
|
||||
// Show until 100% OR run completed
|
||||
if (currentRun.status === 'completed' && percentage === 100) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-gradient-to-r from-brand-50 to-brand-100 border-2 border-brand-300 rounded-xl p-4 mb-6">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<BoltIcon className="w-5 h-5 text-brand-600 animate-pulse" />
|
||||
<span className="font-bold text-brand-800">Full Pipeline Progress</span>
|
||||
</div>
|
||||
<span className="text-2xl font-bold text-brand-600">{percentage}%</span>
|
||||
</div>
|
||||
|
||||
{/* Segmented progress bar showing all 7 stages */}
|
||||
<div className="flex h-4 rounded-full overflow-hidden bg-gray-200">
|
||||
{[1, 2, 3, 4, 5, 6, 7].map(stageNum => {
|
||||
const stageConfig = STAGE_CONFIG[stageNum - 1];
|
||||
const result = currentRun[`stage_${stageNum}_result`];
|
||||
const stageComplete = currentRun.current_stage > stageNum;
|
||||
const isActive = currentRun.current_stage === stageNum;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={stageNum}
|
||||
className={`flex-1 transition-all duration-500 ${
|
||||
stageComplete ? `bg-gradient-to-r ${stageConfig.color}` :
|
||||
isActive ? `bg-gradient-to-r ${stageConfig.color} opacity-60 animate-pulse` :
|
||||
'bg-gray-300'
|
||||
}`}
|
||||
title={`Stage ${stageNum}: ${stageConfig.name}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-xs text-gray-600 mt-2">
|
||||
<span>{completed} / {total} items processed</span>
|
||||
<span>Stage {currentRun.current_stage} of 7</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### 2.4 Consolidate API Calls
|
||||
|
||||
**File:** AutomationPage.tsx
|
||||
|
||||
Replace 17 separate API calls with single unified endpoint:
|
||||
|
||||
```typescript
|
||||
// Current (17 calls):
|
||||
const [keywordsTotalRes, keywordsNewRes, keywordsMappedRes, ...14 more] = await Promise.all([...]);
|
||||
|
||||
// New (1 call):
|
||||
const progressData = await automationService.getRunProgress(activeSite.id, currentRun?.run_id);
|
||||
// Response contains everything: metrics, stage counts, progress data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Stage Card Redesign
|
||||
|
||||
#### 3.1 New Stage Card Layout
|
||||
|
||||
Each stage card shows:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ Stage 1 [ICON] ● Active │
|
||||
│ Keywords → Clusters │
|
||||
├────────────────────────────────────────────┤
|
||||
│ Total Items: 50 │
|
||||
│ Processed: 32 ████████░░ 64% │
|
||||
│ Remaining: 18 │
|
||||
├────────────────────────────────────────────┤
|
||||
│ Output Created: 8 clusters │
|
||||
│ Credits Used: 24 │
|
||||
│ Duration: 4m 32s │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3.2 Status Badge Logic
|
||||
|
||||
```typescript
|
||||
const getStageStatus = (stageNum: number, currentRun: AutomationRun | null) => {
|
||||
if (!currentRun) {
|
||||
// No run - show if items pending
|
||||
return pipelineOverview[stageNum - 1]?.pending > 0 ? 'ready' : 'empty';
|
||||
}
|
||||
|
||||
if (currentRun.current_stage > stageNum) return 'completed';
|
||||
if (currentRun.current_stage === stageNum) return 'active';
|
||||
if (currentRun.current_stage < stageNum) {
|
||||
// Check if previous stage produced items for this stage
|
||||
const prevResult = currentRun[`stage_${stageNum - 1}_result`];
|
||||
if (prevResult?.output_count > 0) return 'ready';
|
||||
return 'pending';
|
||||
}
|
||||
return 'pending';
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Real-time Updates Optimization
|
||||
|
||||
#### 4.1 Smart Polling with Exponential Backoff
|
||||
|
||||
```typescript
|
||||
// Current: Fixed 5s interval
|
||||
const interval = setInterval(loadData, 5000);
|
||||
|
||||
// New: Adaptive polling
|
||||
const useSmartPolling = (isRunning: boolean) => {
|
||||
const [pollInterval, setPollInterval] = useState(2000);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRunning) {
|
||||
setPollInterval(30000); // Slow poll when idle
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast poll during active run, slow down as stage progresses
|
||||
const progressPercent = /* current stage progress */;
|
||||
if (progressPercent < 50) {
|
||||
setPollInterval(2000); // 2s when lots happening
|
||||
} else if (progressPercent < 90) {
|
||||
setPollInterval(3000); // 3s mid-stage
|
||||
} else {
|
||||
setPollInterval(1000); // 1s near completion for responsive transition
|
||||
}
|
||||
}, [isRunning, progressPercent]);
|
||||
|
||||
return pollInterval;
|
||||
};
|
||||
```
|
||||
|
||||
#### 4.2 Optimistic UI Updates
|
||||
|
||||
When user clicks "Run Now":
|
||||
1. Immediately show GlobalProgressBar at 0%
|
||||
2. Immediately set Stage 1 to "Active"
|
||||
3. Don't wait for API confirmation
|
||||
|
||||
---
|
||||
|
||||
## 📋 DETAILED CHECKLIST
|
||||
|
||||
### Backend Tasks
|
||||
- [x] Fix `_get_stage_3_state()` status filter: `'approved'` → `'new'` ✅ DONE
|
||||
- [x] Fix `_get_stage_4_state()` status filter: `'ready'` → `'queued'` ✅ DONE
|
||||
- [x] Create `_get_processed_for_stage(stage_num)` helper ✅ DONE (renamed to `_get_processed_count`)
|
||||
- [x] Add `initial_snapshot` JSON field to `AutomationRun` model ✅ DONE
|
||||
- [x] Capture initial snapshot in `start_automation()` ✅ DONE
|
||||
- [ ] Update snapshot after each stage completes (for cascading stages)
|
||||
- [x] Create new `run_progress` endpoint with unified schema ✅ DONE
|
||||
- [x] Add migration for new model field ✅ DONE (0006_automationrun_initial_snapshot.py)
|
||||
|
||||
### Frontend Tasks
|
||||
- [x] Create `GlobalProgressBar` component ✅ DONE
|
||||
- [x] Add `GlobalProgressBar` to AutomationPage (below metrics, above CurrentProcessingCard) ✅ DONE
|
||||
- [x] Fix `getProcessedFromResult()` helper to extract stage-specific counts ✅ DONE
|
||||
- [x] Update stage card progress calculations ✅ DONE
|
||||
- [x] Update `CurrentProcessingCard` progress calculations ✅ DONE
|
||||
- [x] Add `getRunProgress` method to automationService.ts ✅ DONE
|
||||
- [ ] Consolidate metrics API calls to single endpoint
|
||||
- [ ] Implement smart polling with adaptive intervals
|
||||
- [ ] Add optimistic UI updates for "Run Now" action
|
||||
- [x] Fix "Remaining" count to be `Total - Processed` not `Pending` ✅ DONE
|
||||
|
||||
### Testing
|
||||
- [ ] Test all 7 stages complete correctly
|
||||
- [ ] Verify counts match between stage cards and processing card
|
||||
- [ ] Test pause/resume preserves progress correctly
|
||||
- [ ] Test page refresh during run shows correct state
|
||||
- [ ] Test global progress bar persists until 100%
|
||||
- [ ] Load test: Verify API efficiency improvement
|
||||
|
||||
---
|
||||
|
||||
## 🎯 SUCCESS CRITERIA
|
||||
|
||||
1. **Accurate Counts:** All stage cards show correct Total/Processed/Remaining
|
||||
2. **Consistent Data:** CurrentProcessingCard and Stage Cards show same numbers
|
||||
3. **Global Visibility:** Users see full pipeline progress at all times during run
|
||||
4. **Persistent Progress:** Progress bar stays visible until 100% complete
|
||||
5. **Real-time Feel:** Updates appear within 2-3 seconds of actual progress
|
||||
6. **API Efficiency:** Reduce API calls from 17+ to 1-2 per refresh cycle
|
||||
|
||||
---
|
||||
|
||||
## 🔄 MIGRATION PATH
|
||||
|
||||
1. **Phase 1 (Day 1):** Backend status fixes + new processed count logic
|
||||
2. **Phase 2 (Day 2):** Frontend progress calculation fixes
|
||||
3. **Phase 3 (Day 3):** Global Progress Bar + API consolidation
|
||||
4. **Phase 4 (Day 4):** Smart polling + optimistic updates
|
||||
5. **Phase 5 (Day 5):** Testing + bug fixes
|
||||
|
||||
---
|
||||
|
||||
This plan provides a clear, implementable path to fix all automation page issues. Each phase can be implemented independently, and the plan contains enough detail that any AI model or developer can execute it in a future session.
|
||||
|
||||
---
|
||||
|
||||
#Implementation status
|
||||
|
||||
--
|
||||
|
||||
## ✅ Verification Complete
|
||||
|
||||
### Migration Applied Successfully
|
||||
```
|
||||
Running migrations: Applying automation.0006_automationrun_initial_snapshot... OK
|
||||
```
|
||||
|
||||
### Verification Against Plan Requirements
|
||||
|
||||
| Plan Requirement | Status | Implementation |
|
||||
|-----------------|--------|----------------|
|
||||
| **Fix _get_stage_3_state** (approved→new) | ✅ | automation_service.py: `status='new'` |
|
||||
| **Fix _get_stage_4_state** (ready→queued) | ✅ | automation_service.py: `status='queued'` |
|
||||
| **Fix _get_processed_count** | ✅ | automation_service.py: Uses stage-specific keys |
|
||||
| **Add initial_snapshot field** | ✅ | Migration applied, field exists in model |
|
||||
| **Capture snapshot on run start** | ✅ | automation_service.py: `_capture_initial_snapshot()` called |
|
||||
| **New run_progress endpoint** | ✅ | views.py: `/run_progress/` endpoint |
|
||||
| **GlobalProgressBar component** | ✅ | GlobalProgressBar.tsx created |
|
||||
| **Fix frontend progress calculations** | ✅ | Both AutomationPage and CurrentProcessingCard use `getProcessedFromResult()` |
|
||||
| **getRunProgress in automationService** | ✅ | automationService.ts |
|
||||
|
||||
### Alignment with Your Initial Instructions
|
||||
|
||||
Your requirements from the plan file:
|
||||
1. ✅ **"wrong queue items"** → Fixed status mismatches (approved→new, ready→queued)
|
||||
2. ✅ **"missing queue items"** → Consistent status filters across all views
|
||||
3. ✅ **"progress bar not properly progress"** → Fixed `getProcessedFromResult()` to use correct keys
|
||||
4. ✅ **"total in queue and processed counts buggy"** → Uses initial_snapshot for accurate totals
|
||||
5. ✅ **"manual run full all stages progress bar"** → GlobalProgressBar component shows all 7 stages
|
||||
6. ✅ **"always keep showing unless 100% complete"** → Component hides only when `status === 'completed' && percentage >= 100`
|
||||
|
||||
### AI Functions NOT Modified
|
||||
All AI functions remain unchanged:
|
||||
- auto_cluster.py - ✅ Untouched
|
||||
- generate_ideas.py - ✅ Untouched
|
||||
- generate_content.py - ✅ Untouched
|
||||
- generate_image_prompts.py - ✅ Untouched
|
||||
- generate_images.py - ✅ Untouched
|
||||
- optimize_content.py - ✅ Untouched
|
||||
|
||||
The changes only affect **progress tracking and display**, not the actual AI processing logic.
|
||||
Reference in New Issue
Block a user