This commit is contained in:
alorig
2025-11-09 19:07:06 +05:00
parent 6b738d1ab7
commit 8cd036d8ce
13 changed files with 929 additions and 0 deletions

749
docs/01-ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,749 @@
# IGNY8 System Architecture
**Version:** 1.0
**Last Updated:** 2025-01-XX
**Purpose:** Complete system architecture documentation covering design patterns, principles, tech stack, and structural organization.
---
## Table of Contents
1. [System Overview](#system-overview)
2. [Tech Stack](#tech-stack)
3. [Core Architecture Principles](#core-architecture-principles)
4. [Project Structure](#project-structure)
5. [Key Architectural Patterns](#key-architectural-patterns)
6. [Multi-Tenancy Architecture](#multi-tenancy-architecture)
7. [Module Organization](#module-organization)
8. [API Architecture](#api-architecture)
9. [Frontend Architecture](#frontend-architecture)
10. [Backend Architecture](#backend-architecture)
11. [Database Architecture](#database-architecture)
12. [Security Architecture](#security-architecture)
13. [Deployment Architecture](#deployment-architecture)
---
## System Overview
**IGNY8** is a full-stack SaaS platform for SEO keyword management and AI-driven content generation. The system migrated from a WordPress plugin architecture to a modern Django + React architecture, providing a scalable, multi-account platform for content planning and generation.
### Core Capabilities
- **Multi-Account SaaS Platform**: Complete account isolation with site/sector hierarchy
- **SEO Keyword Management**: Import, organize, and cluster keywords
- **AI-Powered Content Planning**: Automated keyword clustering and content idea generation
- **AI Content Generation**: Automated blog post and article generation
- **Image Generation**: AI-powered image generation for content
- **WordPress Integration**: Publish content directly to WordPress sites
- **Subscription Management**: Plan-based limits and billing integration
---
## Tech Stack
### Backend
- **Framework**: Django 5.2+ with Django REST Framework (DRF)
- **Database**: PostgreSQL
- **Task Queue**: Celery with Redis broker
- **Authentication**: JWT (JSON Web Tokens) + Session-based auth
- **API**: RESTful API with DRF ViewSets
- **Caching**: Redis
- **File Storage**: Local filesystem (configurable for S3)
### Frontend
- **Framework**: React 19 with TypeScript
- **Build Tool**: Vite
- **Styling**: Tailwind CSS
- **State Management**: Zustand
- **Routing**: React Router v6
- **HTTP Client**: Fetch API with custom wrapper
- **UI Components**: Custom component library
### Infrastructure
- **Containerization**: Docker & Docker Compose
- **Reverse Proxy**: Caddy (HTTPS on port 443)
- **Process Management**: Supervisor (for Celery workers)
- **Monitoring**: Portainer (optional)
### Development Tools
- **Backend**: Python 3.11+, pip, virtualenv
- **Frontend**: Node.js 18+, npm/yarn
- **Version Control**: Git
- **Code Quality**: ESLint, Prettier (frontend), Black, Flake8 (backend)
---
## Core Architecture Principles
### 1. Configuration-Driven Everything
**Principle**: Zero HTML/JSX duplication - All UI rendered from configuration.
- **Tables, filters, forms** all driven by config files
- **Single source of truth** - Change config, UI updates everywhere
- **Page-local config** for page-specific settings
- **Shared snippets** for reusable column/filter/action definitions
**Implementation**:
- Frontend: Config files in `/config/pages/` and `/config/snippets/`
- Backend: DRF serializers and ViewSet actions
### 2. Multi-Tenancy Foundation
**Principle**: Complete account isolation with automatic filtering.
- All models inherit `AccountBaseModel` with automatic account isolation
- All ViewSets inherit `AccountModelViewSet` with automatic account filtering
- Middleware injects account context from JWT on every request
- Site > Sector hierarchy for content organization
**Implementation**:
- Base models: `AccountBaseModel`, `SiteSectorBaseModel`
- Base ViewSets: `AccountModelViewSet`, `SiteSectorModelViewSet`
- Middleware: `AccountContextMiddleware` sets `request.account`
### 3. Template System (4 Universal Templates)
**Principle**: Reusable templates for all page types.
- **DashboardTemplate**: Module home pages (KPIs, workflow steps, charts)
- **TablePageTemplate**: CRUD table pages (Keywords, Clusters, Tasks, etc.)
- **FormPageTemplate**: Settings/form pages (Settings, Integration, etc.)
- **SystemPageTemplate**: System/admin pages (Logs, Status, Monitoring)
**Implementation**:
- Frontend: `/templates/` directory with 4 template components
- Config-driven: Templates accept config objects for customization
### 4. Unified AI Processor
**Principle**: Single interface for all AI operations.
- Single `AIProcessor` class handles all AI operations
- Manual and automated workflows use same functions
- Action-based routing: 'clustering', 'ideas', 'content_generation', 'image_generation'
- Account-specific API keys and model configuration
**Implementation**:
- Backend: `AIProcessor` class in `/utils/ai_processor.py`
- Integration: Loads API keys from `IntegrationSettings` model
### 5. Module-Based Organization
**Principle**: Clear module boundaries with shared utilities.
- Each module = Django app (`/modules/{module}/`)
- Clear module boundaries with shared utilities
- Module router pattern for subpage routing
- Consistent structure across modules
**Implementation**:
- Backend: `/modules/planner/`, `/modules/writer/`, `/modules/system/`, `/modules/billing/`
- Frontend: `/pages/Planner/`, `/pages/Writer/`, `/pages/Settings/`, etc.
---
## Project Structure
```
igny8/
├── backend/ # Django backend
│ └── igny8_core/ # Django project
│ ├── auth/ # Multi-tenancy, User, Account, Plan models
│ │ ├── models.py # Account, User, Plan, Site, Sector, Industry models
│ │ ├── views.py # Account, User, Site, Sector ViewSets
│ │ ├── serializers.py # Account, User, Plan serializers
│ │ └── urls.py # Auth module URLs
│ ├── modules/ # Feature modules
│ │ ├── planner/ # Keywords, Clusters, Ideas
│ │ │ ├── models.py # Keywords, Clusters, ContentIdeas models
│ │ │ ├── views.py # KeywordViewSet, ClusterViewSet, ContentIdeasViewSet
│ │ │ ├── tasks.py # Celery tasks for AI operations
│ │ │ ├── serializers.py # Model serializers
│ │ │ └── urls.py # Planner module URLs
│ │ ├── writer/ # Tasks, Content, Images
│ │ │ ├── models.py # Tasks, Content, Images models
│ │ │ ├── views.py # TasksViewSet
│ │ │ ├── tasks.py # Celery tasks for content/image generation
│ │ │ └── urls.py # Writer module URLs
│ │ ├── system/ # Settings, Prompts, Integration
│ │ │ ├── models.py # AIPrompt, IntegrationSettings, AuthorProfile, Strategy
│ │ │ ├── views.py # AIPromptViewSet, AuthorProfileViewSet
│ │ │ ├── integration_views.py # IntegrationSettingsViewSet, task_progress
│ │ │ ├── utils.py # Default prompts, prompt loading
│ │ │ └── urls.py # System module URLs
│ │ └── billing/ # Credits, Transactions, Usage
│ │ ├── models.py # CreditTransaction, UsageLog models
│ │ ├── views.py # Billing ViewSets
│ │ └── services.py # CreditService
│ ├── api/ # API base classes
│ │ └── base.py # AccountModelViewSet, SiteSectorModelViewSet
│ ├── utils/ # Shared utilities
│ │ ├── ai_processor.py # Unified AI interface
│ │ └── content_normalizer.py # Content processing utilities
│ ├── middleware/ # Custom middleware
│ │ ├── account.py # AccountContextMiddleware (sets request.account)
│ │ └── resource_tracker.py # ResourceTrackerMiddleware (API metrics)
│ ├── settings.py # Django settings
│ ├── urls.py # Root URL configuration
│ └── celery.py # Celery configuration
├── frontend/ # React frontend
│ └── src/
│ ├── pages/ # Page components
│ │ ├── Planner/ # KeywordsPage, ClustersPage, IdeasPage, Dashboard
│ │ ├── Writer/ # TasksPage, DraftsPage, PublishedPage, Dashboard
│ │ ├── Settings/ # General, Integration, Status, ImportExport
│ │ ├── Billing/ # Credits, Transactions, Usage
│ │ └── AuthPages/ # SignIn, SignUp
│ ├── templates/ # 4 master templates
│ │ ├── DashboardTemplate.tsx
│ │ ├── TablePageTemplate.tsx
│ │ ├── FormPageTemplate.tsx
│ │ └── SystemPageTemplate.tsx
│ ├── components/ # UI components
│ │ ├── layout/ # AppLayout, Sidebar, Header, Breadcrumbs
│ │ ├── table/ # DataTable, Filters, Actions, Pagination
│ │ ├── ui/ # Button, Card, Modal, Toast, etc.
│ │ └── auth/ # ProtectedRoute, Auth components
│ ├── config/ # Configuration files
│ │ ├── pages/ # Page-specific configs
│ │ │ └── keywords.config.tsx
│ │ ├── snippets/ # Shared column/filter/action definitions
│ │ │ ├── columns.snippets.ts
│ │ │ ├── filters.snippets.ts
│ │ │ └── actions.snippets.ts
│ │ └── routes.config.ts # Route configuration
│ ├── store/ # Zustand stores
│ │ ├── authStore.ts # Authentication state
│ │ ├── plannerStore.ts # Planner module state
│ │ ├── siteStore.ts # Site selection state
│ │ └── aiRequestLogsStore.ts # AI request/response logs
│ ├── services/ # API clients
│ │ └── api.ts # fetchAPI, API functions
│ ├── hooks/ # Custom React hooks
│ │ ├── useProgressModal.ts # Progress modal for long-running tasks
│ │ └── useAuth.ts # Authentication hook
│ ├── layout/ # Layout components
│ │ └── AppLayout.tsx # Main app layout wrapper
│ ├── App.tsx # Root component with routing
│ └── main.tsx # Entry point
└── docs/ # Documentation
└── ActiveDocs/ # Active documentation
├── 01-ARCHITECTURE.md # This file
├── 02-FRONTEND.md # Frontend documentation
├── 03-BACKEND.md # Backend documentation
├── 04-AI-FUNCTIONS.md # AI functions documentation
└── 05-ACCOUNT-USER-PLAN.md # Account/User/Plan documentation
```
---
## Key Architectural Patterns
### Configuration System (Page-Local Config + Shared Snippets)
**Rule**: Config = Page-Local, Snippets = Shared
**Structure**:
```
/pages/Planner/KeywordsPage.tsx
├── Imports snippets from /config/snippets/
├── Defines page-local tableConfig, filterConfig, actionsConfig
└── Passes config to TablePageTemplate
/config/snippets/
├── columns.snippets.ts # statusColumn, titleColumn, etc.
├── filters.snippets.ts # statusFilter, dateRangeFilter, etc.
├── actions.snippets.ts # commonActions, bulkActions, etc.
```
**Benefits**:
- Reusable components across pages
- Page-specific customization
- Single source of truth for shared definitions
### Base ViewSet Pattern
**Backend Pattern**: All ViewSets inherit from base classes for consistent behavior.
**Base Classes**:
- `AccountModelViewSet`: Automatic account filtering
- `SiteSectorModelViewSet`: Account + site/sector filtering + access control
**Benefits**:
- Consistent access control
- Automatic account isolation
- Reduced code duplication
### Celery Task Pattern
**Pattern**: Long-running operations use Celery tasks with progress tracking.
**Structure**:
1. API endpoint queues Celery task
2. Task updates progress via `update_state()`
3. Frontend polls `task_progress` endpoint
4. Progress displayed in modal
**Benefits**:
- Non-blocking API responses
- Real-time progress updates
- Scalable background processing
### AI Function Pattern
**Pattern**: All AI functions follow consistent structure.
**Structure**:
1. API Endpoint: Validates input, queues Celery task
2. Celery Task: Wraps core function with progress tracking
3. Core Function: Business logic, calls AIProcessor
4. AIProcessor: Makes API calls, returns structured data
5. Core Function: Saves results to database
**Benefits**:
- Consistent error handling
- Progress tracking
- Reusable AI interface
---
## Multi-Tenancy Architecture
### Account Isolation
**Principle**: All data is isolated by account.
**Implementation**:
- All models inherit `AccountBaseModel` (has `account` ForeignKey)
- All ViewSets inherit `AccountModelViewSet` (filters by `request.account`)
- Middleware sets `request.account` from JWT token
**Access Control**:
- Admin/Developer users: Bypass account filtering (see all accounts)
- System account users: Bypass account filtering (see all accounts)
- Regular users: Only see data from their account
### Site/Sector Hierarchy
**Structure**:
```
Account (1) ──< (N) Site
Site (1) ──< (1-5) Sector
Sector (1) ──< (N) Keywords, Clusters, ContentIdeas, Tasks
```
**Implementation**:
- Models inherit `SiteSectorBaseModel` (has `site` and `sector` ForeignKeys)
- ViewSets inherit `SiteSectorModelViewSet` (filters by accessible sites)
- User access control via `User.get_accessible_sites()`
**Site Access Control**:
- System account users: All active sites
- Developers: All active sites
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites granted via `SiteUserAccess`
---
## Module Organization
### Planner Module
**Purpose**: Keyword management and content planning.
**Models**:
- `Keywords`: Individual keywords with volume, difficulty, intent
- `Clusters`: Keyword clusters (groups of related keywords)
- `ContentIdeas`: Content ideas generated from clusters
**ViewSets**:
- `KeywordViewSet`: CRUD + `auto_cluster` action
- `ClusterViewSet`: CRUD + `auto_generate_ideas` action
- `ContentIdeasViewSet`: CRUD operations
**Tasks**:
- `auto_cluster_keywords_task`: AI-powered keyword clustering
- `auto_generate_ideas_task`: AI-powered content idea generation
### Writer Module
**Purpose**: Content generation and management.
**Models**:
- `Tasks`: Content generation tasks
- `Content`: Generated content (HTML)
- `Images`: Generated images for tasks
**ViewSets**:
- `TasksViewSet`: CRUD + `auto_generate_content`, `auto_generate_images` actions
**Tasks**:
- `auto_generate_content_task`: AI-powered content generation
- `auto_generate_images_task`: AI-powered image generation
### System Module
**Purpose**: System settings, prompts, and integrations.
**Models**:
- `AIPrompt`: AI prompt templates (clustering, ideas, content, images)
- `IntegrationSettings`: API keys and configuration (OpenAI, Runware, etc.)
- `AuthorProfile`: Writing style profiles
- `Strategy`: Content strategies per sector
**ViewSets**:
- `AIPromptViewSet`: CRUD for prompts
- `IntegrationSettingsViewSet`: CRUD + `test_openai`, `test_runware`, `generate_image`, `task_progress` actions
- `AuthorProfileViewSet`: CRUD for author profiles
- `StrategyViewSet`: CRUD for strategies
### Billing Module
**Purpose**: Credits, transactions, and usage tracking.
**Models**:
- `CreditTransaction`: Credit purchase/usage transactions
- `UsageLog`: Daily/monthly usage tracking
**ViewSets**:
- `CreditTransactionViewSet`: CRUD for transactions
- `UsageLogViewSet`: Read-only usage logs
**Services**:
- `CreditService`: Credit calculation and deduction logic
---
## API Architecture
### RESTful API Design
**Base URL**: `/api/v1/`
**Endpoint Structure**:
- `/api/v1/planner/keywords/` - Keywords CRUD
- `/api/v1/planner/keywords/auto_cluster/` - Auto-cluster action
- `/api/v1/planner/clusters/` - Clusters CRUD
- `/api/v1/planner/clusters/auto_generate_ideas/` - Auto-generate ideas action
- `/api/v1/writer/tasks/` - Tasks CRUD
- `/api/v1/writer/tasks/auto_generate_content/` - Auto-generate content action
- `/api/v1/system/settings/task_progress/{task_id}/` - Task progress polling
### Authentication
**Methods**:
- JWT (JSON Web Tokens) - Primary method
- Session-based auth - Fallback for admin
**Flow**:
1. User signs in → Backend returns JWT token
2. Frontend stores token in localStorage
3. Frontend includes token in `Authorization: Bearer {token}` header
4. Backend middleware validates token and sets `request.user` and `request.account`
### Response Format
**Success Response**:
```json
{
"success": true,
"data": { ... },
"message": "Optional message"
}
```
**Error Response**:
```json
{
"success": false,
"message": "Error message",
"errors": { ... }
}
```
### Pagination
**Format**: Page-based pagination
**Response**:
```json
{
"count": 100,
"next": "http://api.example.com/api/v1/resource/?page=2",
"previous": null,
"results": [ ... ]
}
```
---
## Frontend Architecture
### Component Hierarchy
```
App
└── AppLayout
├── Sidebar (navigation)
├── Header (user menu, notifications)
└── Main Content
└── Page Component
└── Template (DashboardTemplate, TablePageTemplate, etc.)
└── Components (DataTable, Filters, etc.)
```
### State Management
**Zustand Stores**:
- `authStore`: Authentication state (user, token, account)
- `plannerStore`: Planner module state
- `siteStore`: Selected site/sector
- `aiRequestLogsStore`: AI request/response logs
- `pageSizeStore`: Table page size preference
**Local State**: React `useState` for component-specific state
### Routing
**Structure**: React Router v6 with nested routes
**Routes**:
- `/` - Home/Dashboard
- `/planner` - Planner Dashboard
- `/planner/keywords` - Keywords page
- `/planner/clusters` - Clusters page
- `/planner/ideas` - Ideas page
- `/writer` - Writer Dashboard
- `/writer/tasks` - Tasks page
- `/settings` - Settings pages
**Protected Routes**: All routes except `/signin` and `/signup` require authentication
---
## Backend Architecture
### Model Inheritance Hierarchy
```
models.Model
└── AccountBaseModel (adds account ForeignKey)
└── SiteSectorBaseModel (adds site, sector ForeignKeys)
└── Keywords, Clusters, ContentIdeas, Tasks, etc.
```
### ViewSet Inheritance Hierarchy
```
viewsets.ModelViewSet
└── AccountModelViewSet (adds account filtering)
└── SiteSectorModelViewSet (adds site/sector filtering)
└── KeywordViewSet, ClusterViewSet, TasksViewSet, etc.
```
### Middleware Stack
1. **SecurityMiddleware**: Django security middleware
2. **SessionMiddleware**: Session management
3. **AuthenticationMiddleware**: User authentication
4. **AccountContextMiddleware**: Sets `request.account` from JWT
5. **ResourceTrackerMiddleware**: Tracks API request metrics
### Celery Task Architecture
**Broker**: Redis
**Workers**: Separate Celery worker processes
**Task Structure**:
```python
@shared_task(bind=True)
def my_task(self, ...):
# Update progress
self.update_state(state='PROGRESS', meta={...})
# Do work
result = do_work()
# Return result
return result
```
**Progress Tracking**:
- Frontend polls `/api/v1/system/settings/task_progress/{task_id}/`
- Backend returns task state and meta information
- Progress displayed in modal
---
## Database Architecture
### Core Tables
- `igny8_accounts`: Account information
- `igny8_users`: User accounts
- `igny8_plans`: Subscription plans
- `igny8_subscriptions`: Active subscriptions
- `igny8_sites`: Sites within accounts
- `igny8_sectors`: Sectors within sites
- `igny8_industries`: Global industry templates
- `igny8_industry_sectors`: Industry sector templates
### Planner Tables
- `igny8_keywords`: Keywords
- `igny8_clusters`: Keyword clusters
- `igny8_content_ideas`: Content ideas
### Writer Tables
- `igny8_tasks`: Content generation tasks
- `igny8_content`: Generated content
- `igny8_images`: Generated images
### System Tables
- `igny8_ai_prompts`: AI prompt templates
- `igny8_integration_settings`: API keys and configuration
- `igny8_author_profiles`: Writing style profiles
- `igny8_strategies`: Content strategies
### Billing Tables
- `igny8_credit_transactions`: Credit transactions
- `igny8_usage_logs`: Usage tracking
### Indexes
**Account Isolation**: All tables have indexes on `account`
**Site/Sector Filtering**: Tables with site/sector have composite indexes on `(account, site, sector)`
**Performance**: Indexes on frequently queried fields (status, created_at, etc.)
---
## Security Architecture
### Authentication
**JWT Tokens**:
- Signed with secret key
- Contains user ID and account ID
- Expires after configured time
- Stored in localStorage (frontend)
**Session Auth**:
- Fallback for admin interface
- Django session framework
### Authorization
**Role-Based Access Control (RBAC)**:
- `developer`: Full system access
- `owner`: Full account access
- `admin`: Account admin access
- `editor`: Content editing access
- `viewer`: Read-only access
**Access Control**:
- Account-level: Automatic filtering by `request.account`
- Site-level: Filtering by `user.get_accessible_sites()`
- Action-level: Permission checks in ViewSet actions
### Data Isolation
**Account Isolation**:
- All queries filtered by account
- Admin/Developer override for system accounts
**Site Access Control**:
- Users can only access granted sites
- Admin/Developer override for all sites
### API Security
**CORS**: Configured for frontend domain
**CSRF**: Enabled for session-based auth
**Rate Limiting**: (Future implementation)
**Input Validation**: DRF serializers validate all input
---
## Deployment Architecture
### Docker Compose Setup
**Services**:
- `backend`: Django application (port 8010/8011)
- `frontend`: React application (port 5173/8021)
- `db`: PostgreSQL database
- `redis`: Redis for Celery broker and caching
- `caddy`: Reverse proxy (HTTPS on port 443)
- `celery-worker`: Celery worker process
- `celery-beat`: Celery beat scheduler (optional)
### Environment Configuration
**Backend**:
- `DJANGO_SETTINGS_MODULE`: Django settings module
- `DATABASE_URL`: PostgreSQL connection string
- `REDIS_URL`: Redis connection string
- `SECRET_KEY`: Django secret key
- `OPENAI_API_KEY`: OpenAI API key (fallback)
**Frontend**:
- `VITE_API_URL`: Backend API URL
- `VITE_APP_NAME`: Application name
### Scaling Considerations
**Horizontal Scaling**:
- Multiple Celery workers
- Multiple backend instances (load balanced)
- Multiple frontend instances (static files)
**Vertical Scaling**:
- Database connection pooling
- Redis connection pooling
- Celery worker concurrency
### Monitoring
**Application Monitoring**:
- ResourceTrackerMiddleware tracks API request metrics
- Celery task monitoring via Flower (optional)
**Infrastructure Monitoring**:
- Portainer for container monitoring
- Database monitoring via PostgreSQL logs
- Redis monitoring via Redis CLI
---
## Summary
The IGNY8 architecture is built on:
1. **Configuration-Driven Design**: Zero duplication, single source of truth
2. **Multi-Tenancy Foundation**: Complete account isolation with site/sector hierarchy
3. **Template System**: 4 universal templates for all page types
4. **Unified AI Interface**: Single AIProcessor for all AI operations
5. **Module-Based Organization**: Clear boundaries with shared utilities
6. **RESTful API**: Consistent API design with DRF
7. **Modern Frontend**: React + TypeScript with Zustand state management
8. **Scalable Backend**: Django + Celery for async processing
9. **Security First**: JWT auth, RBAC, data isolation
10. **Docker Deployment**: Containerized for easy deployment and scaling
This architecture ensures scalability, maintainability, and extensibility while maintaining a clean separation of concerns across modules.

776
docs/02-FRONTEND.md Normal file
View File

@@ -0,0 +1,776 @@
# IGNY8 Frontend Documentation
**Version:** 1.0
**Last Updated:** 2025-01-XX
**Purpose:** Complete frontend documentation covering pages, components, templates, routing, state management, and configuration system.
---
## Table of Contents
1. [Frontend Overview](#frontend-overview)
2. [Tech Stack](#tech-stack)
3. [Project Structure](#project-structure)
4. [Routing System](#routing-system)
5. [Template System](#template-system)
6. [Component Library](#component-library)
7. [State Management](#state-management)
8. [API Integration](#api-integration)
9. [Configuration System](#configuration-system)
10. [Pages](#pages)
11. [Hooks](#hooks)
12. [Utilities](#utilities)
---
## Frontend Overview
The IGNY8 frontend is a React 19 application built with TypeScript, using Vite as the build tool and Tailwind CSS for styling. The frontend follows a configuration-driven architecture where UI components are rendered from configuration objects, eliminating code duplication and ensuring consistency.
### Key Features
- **Configuration-Driven UI**: All tables, filters, and forms driven by config files
- **4 Universal Templates**: DashboardTemplate, TablePageTemplate, FormPageTemplate, SystemPageTemplate
- **TypeScript**: Full type safety across the application
- **Zustand State Management**: Lightweight, performant state management
- **React Router v6**: Modern routing with nested routes
- **Responsive Design**: Mobile-first approach with Tailwind CSS
---
## Tech Stack
### Core Technologies
- **React 19**: UI library
- **TypeScript**: Type safety
- **Vite**: Build tool and dev server
- **Tailwind CSS**: Utility-first CSS framework
- **React Router v6**: Client-side routing
### State Management
- **Zustand**: Lightweight state management library
- **localStorage**: Persistence for Zustand stores
### HTTP Client
- **Fetch API**: Native browser API with custom wrapper
- **Auto-retry**: Automatic retry on network failures
- **Token refresh**: Automatic JWT token refresh
### UI Components
- **Custom Component Library**: Built-in components (Button, Card, Modal, etc.)
- **Icons**: Custom icon library
- **Toast Notifications**: Toast notification system
---
## Project Structure
```
frontend/src/
├── pages/ # Page components
│ ├── Planner/ # Planner module pages
│ │ ├── Dashboard.tsx
│ │ ├── Keywords.tsx
│ │ ├── Clusters.tsx
│ │ └── Ideas.tsx
│ ├── Writer/ # Writer module pages
│ │ ├── Dashboard.tsx
│ │ ├── Tasks.tsx
│ │ ├── Content.tsx
│ │ ├── Drafts.tsx
│ │ ├── Images.tsx
│ │ └── Published.tsx
│ ├── Settings/ # Settings pages
│ │ ├── General.tsx
│ │ ├── Users.tsx
│ │ ├── Sites.tsx
│ │ ├── Integration.tsx
│ │ └── ...
│ ├── AuthPages/ # Authentication pages
│ │ ├── SignIn.tsx
│ │ └── SignUp.tsx
│ └── ...
├── templates/ # 4 universal templates
│ ├── DashboardTemplate.tsx
│ ├── TablePageTemplate.tsx
│ ├── FormPageTemplate.tsx
│ └── SystemPageTemplate.tsx
├── components/ # UI components
│ ├── layout/ # Layout components
│ │ ├── AppLayout.tsx
│ │ ├── Sidebar.tsx
│ │ └── Header.tsx
│ ├── table/ # Table components
│ │ ├── DataTable.tsx
│ │ ├── Filters.tsx
│ │ └── Pagination.tsx
│ ├── ui/ # UI primitives
│ │ ├── button/
│ │ ├── card/
│ │ ├── modal/
│ │ └── ...
│ └── auth/ # Auth components
│ └── ProtectedRoute.tsx
├── config/ # Configuration files
│ ├── pages/ # Page-specific configs
│ │ └── keywords.config.tsx
│ ├── snippets/ # Shared snippets
│ │ ├── columns.snippets.ts
│ │ ├── filters.snippets.ts
│ │ └── actions.snippets.ts
│ └── routes.config.ts # Route configuration
├── store/ # Zustand stores
│ ├── authStore.ts # Authentication state
│ ├── plannerStore.ts # Planner module state
│ ├── siteStore.ts # Site selection state
│ ├── sectorStore.ts # Sector selection state
│ ├── aiRequestLogsStore.ts # AI request/response logs
│ └── pageSizeStore.ts # Table page size preference
├── services/ # API clients
│ └── api.ts # fetchAPI, API functions
├── hooks/ # Custom React hooks
│ ├── useProgressModal.ts # Progress modal for long-running tasks
│ └── useAuth.ts # Authentication hook
├── layout/ # Layout components
│ └── AppLayout.tsx # Main app layout wrapper
├── utils/ # Utility functions
│ └── difficulty.ts # Difficulty utilities
├── App.tsx # Root component with routing
└── main.tsx # Entry point
```
---
## Routing System
### Route Structure
**File**: `App.tsx`
**Public Routes**:
- `/signin` - Sign in page
- `/signup` - Sign up page
**Protected Routes** (require authentication):
- `/` - Home/Dashboard
- `/planner` - Planner Dashboard
- `/planner/keywords` - Keywords page
- `/planner/clusters` - Clusters page
- `/planner/ideas` - Ideas page
- `/writer` - Writer Dashboard
- `/writer/tasks` - Tasks page
- `/writer/content` - Content page
- `/writer/drafts` - Drafts page
- `/writer/images` - Images page
- `/writer/published` - Published page
- `/thinker` - Thinker Dashboard
- `/thinker/prompts` - Prompts page
- `/thinker/author-profiles` - Author Profiles page
- `/thinker/strategies` - Strategies page
- `/thinker/image-testing` - Image Testing page
- `/billing/credits` - Credits page
- `/billing/transactions` - Transactions page
- `/billing/usage` - Usage page
- `/settings` - Settings pages
- `/analytics` - Analytics page
- `/schedules` - Schedules page
### Route Configuration
**File**: `config/routes.config.ts`
Defines route metadata including:
- Route paths
- Labels for navigation
- Icons
- Breadcrumb labels
- Nested route structure
### Protected Routes
**Component**: `components/auth/ProtectedRoute.tsx`
**Functionality**:
- Checks authentication status from `authStore`
- Redirects to `/signin` if not authenticated
- Wraps protected routes with `AppLayout`
---
## Template System
### 1. DashboardTemplate
**Purpose**: Module home pages with KPIs, workflow steps, and charts.
**Usage**:
```tsx
<DashboardTemplate
title="Planner Dashboard"
subtitle="Manage your SEO keywords and content planning"
metrics={metrics}
workflowSteps={steps}
charts={charts}
/>
```
**Features**:
- Header metrics (KPIs)
- Workflow steps
- Charts and visualizations
- Quick actions
### 2. TablePageTemplate
**Purpose**: CRUD table pages (Keywords, Clusters, Tasks, etc.).
**Usage**:
```tsx
<TablePageTemplate
title="Keywords"
subtitle="Manage and organize SEO keywords"
columns={columns}
data={data}
filters={filterConfig}
onSort={(field, direction) => ...}
onEdit={(row) => ...}
onDelete={(row) => ...}
bulkActions={bulkActions}
/>
```
**Features**:
- Data table with sorting
- Filters (text, select, date range, custom)
- Pagination
- Bulk actions
- Row actions (edit, delete)
- AI Request/Response Logs section
- Import/Export functionality
**Configuration**:
- `columns`: Column definitions (key, label, sortable, render, etc.)
- `filters`: Filter definitions (type, options, custom render)
- `bulkActions`: Bulk action definitions
- `actions`: Row action definitions
### 3. FormPageTemplate
**Purpose**: Settings/form pages (Settings, Integration, etc.).
**Usage**:
```tsx
<FormPageTemplate
title="Settings"
subtitle="Configure your account settings"
sections={sections}
onSubmit={(data) => ...}
/>
```
**Features**:
- Form sections
- Form validation
- Save/Cancel buttons
- Success/Error notifications
### 4. SystemPageTemplate
**Purpose**: System/admin pages (Logs, Status, Monitoring).
**Usage**:
```tsx
<SystemPageTemplate
title="System Status"
subtitle="Monitor system health and performance"
content={content}
/>
```
**Features**:
- System information display
- Logs viewer
- Status indicators
- Performance metrics
---
## Component Library
### Layout Components
#### AppLayout
**File**: `layout/AppLayout.tsx`
**Purpose**: Main app layout wrapper.
**Features**:
- Sidebar navigation
- Header with user menu
- Main content area
- Breadcrumbs
#### Sidebar
**File**: `components/layout/Sidebar.tsx`
**Purpose**: Navigation sidebar.
**Features**:
- Module navigation
- Active route highlighting
- Collapsible sections
#### Header
**File**: `components/layout/Header.tsx`
**Purpose**: Top header bar.
**Features**:
- User menu
- Notifications
- Site/Sector selector
### Table Components
#### DataTable
**File**: `components/table/DataTable.tsx`
**Purpose**: Data table component.
**Features**:
- Sortable columns
- Selectable rows
- Row actions
- Responsive design
#### Filters
**File**: `components/table/Filters.tsx`
**Purpose**: Filter component.
**Features**:
- Text filters
- Select filters
- Date range filters
- Custom filters
#### Pagination
**File**: `components/ui/pagination/CompactPagination.tsx`
**Purpose**: Pagination component.
**Features**:
- Page navigation
- Page size selector
- Total count display
### UI Components
#### Button
**File**: `components/ui/button/Button.tsx`
**Variants**: primary, secondary, danger, ghost, link
**Sizes**: sm, md, lg
#### Card
**File**: `components/ui/card/Card.tsx`
**Purpose**: Card container component.
#### Modal
**File**: `components/ui/modal/Modal.tsx`
**Purpose**: Modal dialog component.
**Variants**: FormModal, ProgressModal, AlertModal
#### Toast
**File**: `components/ui/toast/ToastContainer.tsx`
**Purpose**: Toast notification system.
**Types**: success, error, warning, info
#### Input
**File**: `components/form/input/InputField.tsx`
**Purpose**: Text input component.
#### Select
**File**: `components/form/SelectDropdown.tsx`
**Purpose**: Select dropdown component.
#### Checkbox
**File**: `components/form/input/Checkbox.tsx`
**Purpose**: Checkbox component.
### Auth Components
#### ProtectedRoute
**File**: `components/auth/ProtectedRoute.tsx`
**Purpose**: Route protection component.
**Functionality**:
- Checks authentication
- Redirects to signin if not authenticated
- Wraps children with AppLayout
---
## State Management
### Zustand Stores
#### authStore
**File**: `store/authStore.ts`
**State**:
- `user`: Current user object
- `token`: JWT access token
- `refreshToken`: JWT refresh token
- `isAuthenticated`: Authentication status
- `loading`: Loading state
**Actions**:
- `login(email, password)`: Sign in user
- `logout()`: Sign out user
- `register(data)`: Register new user
- `setUser(user)`: Set user object
- `setToken(token)`: Set access token
- `refreshToken()`: Refresh access token
**Persistence**: localStorage (persisted)
#### plannerStore
**File**: `store/plannerStore.ts`
**State**: Planner module-specific state
**Actions**: Planner module actions
#### siteStore
**File**: `store/siteStore.ts`
**State**:
- `activeSite`: Currently selected site
- `sites`: List of accessible sites
**Actions**:
- `setActiveSite(site)`: Set active site
- `loadSites()`: Load accessible sites
**Persistence**: localStorage (persisted)
#### sectorStore
**File**: `store/sectorStore.ts`
**State**:
- `activeSector`: Currently selected sector
- `sectors`: List of sectors for active site
**Actions**:
- `setActiveSector(sector)`: Set active sector
- `loadSectorsForSite(siteId)`: Load sectors for site
**Persistence**: localStorage (persisted)
#### aiRequestLogsStore
**File**: `store/aiRequestLogsStore.ts`
**State**:
- `logs`: Array of AI request/response logs
**Actions**:
- `addLog(log)`: Add new log entry
- `addRequestStep(logId, step)`: Add request step to log
- `addResponseStep(logId, step)`: Add response step to log
- `updateLog(logId, data)`: Update log entry
- `clearLogs()`: Clear all logs
**Purpose**: Tracks AI function execution with step-by-step logs
#### pageSizeStore
**File**: `store/pageSizeStore.ts`
**State**:
- `pageSize`: Table page size preference
**Actions**:
- `setPageSize(size)`: Set page size
**Persistence**: localStorage (persisted)
---
## API Integration
### API Service
**File**: `services/api.ts`
**Functions**:
- `fetchAPI(url, options)`: Generic API fetch wrapper
- `fetchKeywords(filters)`: Fetch keywords
- `createKeyword(data)`: Create keyword
- `updateKeyword(id, data)`: Update keyword
- `deleteKeyword(id)`: Delete keyword
- `bulkDeleteKeywords(ids)`: Bulk delete keywords
- `autoClusterKeywords(ids)`: Auto-cluster keywords
- `fetchClusters(filters)`: Fetch clusters
- `autoGenerateIdeas(clusterIds)`: Auto-generate ideas
- `fetchTasks(filters)`: Fetch tasks
- `autoGenerateContent(taskIds)`: Auto-generate content
- `autoGenerateImages(taskIds)`: Auto-generate images
- And more...
**Features**:
- Automatic JWT token inclusion
- Automatic token refresh on 401
- Auto-retry on network failures
- Error handling
- Request/response logging
### API Base URL
**Auto-detection**:
- Checks environment variables (`VITE_BACKEND_URL`, `VITE_API_URL`)
- Falls back to auto-detection based on current origin
- Supports localhost, IP addresses, and production subdomain
**Default**: `https://api.igny8.com/api`
---
## Configuration System
### Page-Local Config
**Location**: `config/pages/`
**Example**: `keywords.config.tsx`
**Structure**:
```tsx
export const createKeywordsPageConfig = () => ({
columns: [...],
filters: [...],
bulkActions: [...],
actions: [...],
});
```
**Usage**:
```tsx
import { createKeywordsPageConfig } from '../../config/pages/keywords.config';
const config = createKeywordsPageConfig();
```
### Shared Snippets
**Location**: `config/snippets/`
#### columns.snippets.ts
**Purpose**: Reusable column definitions.
**Examples**:
- `statusColumn`: Status column with badge
- `titleColumn`: Title column with link
- `dateColumn`: Date column with formatting
#### filters.snippets.ts
**Purpose**: Reusable filter definitions.
**Examples**:
- `statusFilter`: Status dropdown filter
- `dateRangeFilter`: Date range filter
- `searchFilter`: Text search filter
#### actions.snippets.ts
**Purpose**: Reusable action definitions.
**Examples**:
- `commonActions`: Edit, Delete actions
- `bulkActions`: Bulk delete, bulk update actions
### Route Configuration
**File**: `config/routes.config.ts`
**Structure**:
```tsx
export const routes: RouteConfig[] = [
{
path: '/planner',
label: 'Planner',
icon: 'Planner',
children: [
{ path: '/planner/keywords', label: 'Keywords' },
...
],
},
...
];
```
**Functions**:
- `getBreadcrumbs(pathname)`: Get breadcrumbs for current route
---
## Pages
### Planner Module
#### Keywords Page
**File**: `pages/Planner/Keywords.tsx`
**Features**:
- Keyword CRUD operations
- Auto-cluster functionality
- Import/Export (CSV)
- Filters (status, cluster, intent, difficulty, volume)
- Bulk actions (delete, status update)
- AI Request/Response Logs
**Configuration**: Uses `keywords.config.tsx`
#### Clusters Page
**File**: `pages/Planner/Clusters.tsx`
**Features**:
- Cluster CRUD operations
- Auto-generate ideas functionality
- Filters (status, sector)
- Bulk actions
#### Ideas Page
**File**: `pages/Planner/Ideas.tsx`
**Features**:
- Content ideas CRUD operations
- Filters (status, cluster, content type)
- Bulk actions
### Writer Module
#### Tasks Page
**File**: `pages/Writer/Tasks.tsx`
**Features**:
- Task CRUD operations
- Auto-generate content functionality
- Auto-generate images functionality
- Filters (status, cluster, content type)
- Bulk actions
### Settings Pages
#### Sites Page
**File**: `pages/Settings/Sites.tsx`
**Features**:
- Site CRUD operations
- Site activation/deactivation
- Multiple sites can be active simultaneously
#### Integration Page
**File**: `pages/Settings/Integration.tsx`
**Features**:
- Integration settings (OpenAI, Runware)
- API key configuration
- Test connections
- Image generation testing
---
## Hooks
### useProgressModal
**File**: `hooks/useProgressModal.ts`
**Purpose**: Progress modal for long-running Celery tasks.
**Usage**:
```tsx
const progressModal = useProgressModal();
// Start task
progressModal.start(taskId, 'Task started...');
// Task progress is automatically polled
// Modal displays progress updates
```
**Features**:
- Automatic polling of task progress
- Progress percentage display
- Step-by-step logs (request/response steps)
- Success/Error handling
- Auto-close on completion
### useAuth
**File**: `hooks/useAuth.ts`
**Purpose**: Authentication hook.
**Usage**:
```tsx
const { user, isAuthenticated, login, logout } = useAuth();
```
**Features**:
- Access to auth state
- Login/logout functions
- Authentication status
---
## Utilities
### Difficulty Utilities
**File**: `utils/difficulty.ts`
**Functions**:
- `getDifficultyLabelFromNumber(number)`: Get difficulty label (Easy, Medium, Hard)
- `getDifficultyRange()`: Get difficulty range options
### API Utilities
**File**: `services/api.ts`
**Helper Functions**:
- `getActiveSiteId()`: Get active site ID from store
- `getActiveSectorId()`: Get active sector ID from store
- `getAuthToken()`: Get JWT token from store
- `getRefreshToken()`: Get refresh token from store
---
## Summary
The IGNY8 frontend is built on:
1. **Configuration-Driven Architecture**: Zero duplication, single source of truth
2. **4 Universal Templates**: Reusable templates for all page types
3. **TypeScript**: Full type safety
4. **Zustand State Management**: Lightweight, performant state management
5. **React Router v6**: Modern routing with nested routes
6. **Component Library**: Reusable UI components
7. **API Integration**: Automatic token handling and error management
8. **Responsive Design**: Mobile-first with Tailwind CSS
This architecture ensures consistency, maintainability, and extensibility while providing a great user experience.

860
docs/03-BACKEND.md Normal file
View File

@@ -0,0 +1,860 @@
# IGNY8 Backend Documentation
**Version:** 1.0
**Last Updated:** 2025-01-XX
**Purpose:** Complete backend documentation covering models, views, APIs, modules, serializers, tasks, and structure.
---
## Table of Contents
1. [Backend Overview](#backend-overview)
2. [Tech Stack](#tech-stack)
3. [Project Structure](#project-structure)
4. [Models](#models)
5. [ViewSets](#viewsets)
6. [Serializers](#serializers)
7. [Celery Tasks](#celery-tasks)
8. [API Endpoints](#api-endpoints)
9. [Base Classes](#base-classes)
10. [Middleware](#middleware)
11. [Utilities](#utilities)
12. [Modules](#modules)
---
## Backend Overview
The IGNY8 backend is a Django 5.2+ application using Django REST Framework (DRF) for API endpoints. The backend follows a modular architecture with clear separation of concerns, automatic account isolation, and support for asynchronous task processing via Celery.
### Key Features
- **Multi-Tenancy**: Complete account isolation with automatic filtering
- **RESTful API**: DRF ViewSets with consistent response format
- **Celery Integration**: Asynchronous task processing for long-running operations
- **Account/Site/Sector Hierarchy**: Hierarchical data organization
- **AI Integration**: Unified AIProcessor for all AI operations
- **Progress Tracking**: Real-time progress updates for Celery tasks
---
## Tech Stack
### Core Technologies
- **Django 5.2+**: Web framework
- **Django REST Framework**: API framework
- **PostgreSQL**: Database
- **Celery**: Asynchronous task queue
- **Redis**: Celery broker and caching
### Key Libraries
- **django-filter**: Advanced filtering
- **djangorestframework-simplejwt**: JWT authentication
- **requests**: HTTP client for external APIs
- **python-dotenv**: Environment variable management
---
## Project Structure
```
backend/igny8_core/
├── auth/ # Multi-tenancy and authentication
│ ├── models.py # Account, User, Plan, Site, Sector, Industry models
│ ├── views.py # Account, User, Site, Sector ViewSets
│ ├── serializers.py # Account, User, Plan serializers
│ └── urls.py # Auth module URLs
├── modules/ # Feature modules
│ ├── planner/ # Keywords, Clusters, Ideas
│ │ ├── models.py # Keywords, Clusters, ContentIdeas models
│ │ ├── views.py # KeywordViewSet, ClusterViewSet, ContentIdeasViewSet
│ │ ├── tasks.py # Celery tasks for AI operations
│ │ ├── serializers.py # Model serializers
│ │ └── urls.py # Planner module URLs
│ ├── writer/ # Tasks, Content, Images
│ │ ├── models.py # Tasks, Content, Images models
│ │ ├── views.py # TasksViewSet
│ │ ├── tasks.py # Celery tasks for content/image generation
│ │ └── urls.py # Writer module URLs
│ ├── system/ # Settings, Prompts, Integration
│ │ ├── models.py # AIPrompt, IntegrationSettings, AuthorProfile, Strategy
│ │ ├── views.py # AIPromptViewSet, AuthorProfileViewSet
│ │ ├── integration_views.py # IntegrationSettingsViewSet, task_progress
│ │ ├── utils.py # Default prompts, prompt loading
│ │ └── urls.py # System module URLs
│ └── billing/ # Credits, Transactions, Usage
│ ├── models.py # CreditTransaction, UsageLog models
│ ├── views.py # Billing ViewSets
│ └── services.py # CreditService
├── api/ # API base classes
│ ├── base.py # AccountModelViewSet, SiteSectorModelViewSet
│ └── pagination.py # CustomPageNumberPagination
├── utils/ # Shared utilities
│ ├── ai_processor.py # Unified AI interface
│ └── content_normalizer.py # Content processing utilities
├── middleware/ # Custom middleware
│ ├── account.py # AccountContextMiddleware (sets request.account)
│ └── resource_tracker.py # ResourceTrackerMiddleware (API metrics)
├── settings.py # Django settings
├── urls.py # Root URL configuration
└── celery.py # Celery configuration
```
---
## Models
### Base Models
#### AccountBaseModel
**File**: `auth/models.py`
**Purpose**: Base model for all account-isolated models.
**Fields**:
- `account`: ForeignKey to Account
- `created_at`: DateTimeField (auto_now_add)
- `updated_at`: DateTimeField (auto_now)
**Usage**: All models that need account isolation inherit from this.
#### SiteSectorBaseModel
**File**: `auth/models.py`
**Purpose**: Base model for models that belong to Site and Sector.
**Fields**:
- Inherits from `AccountBaseModel`
- `site`: ForeignKey to Site
- `sector`: ForeignKey to Sector
**Methods**:
- `save()`: Automatically sets `account` from `site.account` and validates sector belongs to site
**Usage**: Models like Keywords, Clusters, ContentIdeas, Tasks inherit from this.
### Auth Models
#### Account
**File**: `auth/models.py`
**Table**: `igny8_accounts`
**Fields**:
- `name`: CharField
- `slug`: SlugField (unique)
- `owner`: ForeignKey to User
- `stripe_customer_id`: CharField (optional)
- `plan`: ForeignKey to Plan
- `credits`: IntegerField (default: 0)
- `status`: CharField (choices: active, suspended, trial, cancelled)
**Methods**:
- `is_system_account()`: Returns True if account is system account (aws-admin, default-account, default)
#### User
**File**: `auth/models.py`
**Table**: `igny8_users`
**Fields**:
- Inherits from `AbstractUser`
- `email`: EmailField (unique, USERNAME_FIELD)
- `account`: ForeignKey to Account
- `role`: CharField (choices: developer, owner, admin, editor, viewer, system_bot)
**Methods**:
- `has_role(*roles)`: Check if user has any of the specified roles
- `is_owner_or_admin()`: Returns True if role is owner or admin
- `is_developer()`: Returns True if role is developer or is_superuser
- `is_admin_or_developer()`: Returns True if role is admin or developer
- `is_system_account_user()`: Returns True if user belongs to system account
- `get_accessible_sites()`: Returns QuerySet of sites user can access
#### Plan
**File**: `auth/models.py`
**Table**: `igny8_plans`
**Fields**:
- `name`: CharField
- `slug`: SlugField (unique)
- `price`: DecimalField
- `billing_cycle`: CharField (choices: monthly, annual)
- `features`: JSONField (array of feature strings)
- `max_users`: IntegerField
- `max_sites`: IntegerField
- `max_keywords`: IntegerField
- `max_clusters`: IntegerField
- `max_content_ideas`: IntegerField
- `daily_cluster_limit`: IntegerField
- `monthly_cluster_ai_credits`: IntegerField
- `daily_content_tasks`: IntegerField
- `daily_ai_requests`: IntegerField
- `monthly_word_count_limit`: IntegerField
- `monthly_content_ai_credits`: IntegerField
- `monthly_image_count`: IntegerField
- `daily_image_generation_limit`: IntegerField
- `monthly_image_ai_credits`: IntegerField
- `max_images_per_task`: IntegerField
- `image_model_choices`: JSONField
- `included_credits`: IntegerField
- `extra_credit_price`: DecimalField
- And more...
**Methods**:
- `clean()`: Validates plan limits
- `get_effective_credits_per_month()`: Returns included_credits or credits_per_month
#### Site
**File**: `auth/models.py`
**Table**: `igny8_sites`
**Fields**:
- Inherits from `AccountBaseModel`
- `name`: CharField
- `slug`: SlugField (unique per account)
- `domain`: URLField (optional)
- `industry`: ForeignKey to Industry (optional)
- `is_active`: BooleanField
- `status`: CharField (choices: active, inactive, suspended)
- `wp_url`: URLField (optional, WordPress integration)
- `wp_username`: CharField (optional)
- `wp_app_password`: CharField (optional)
**Methods**:
- `get_active_sectors_count()`: Get count of active sectors
- `can_add_sector()`: Check if site can add another sector (max 5)
#### Sector
**File**: `auth/models.py`
**Table**: `igny8_sectors`
**Fields**:
- Inherits from `AccountBaseModel`
- `site`: ForeignKey to Site
- `industry_sector`: ForeignKey to IndustrySector (optional, template reference)
- `name`: CharField
- `slug`: SlugField (unique per site)
- `is_active`: BooleanField
- `status`: CharField (choices: active, inactive)
### Planner Models
#### Keywords
**File**: `modules/planner/models.py`
**Table**: `igny8_keywords`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `keyword`: CharField
- `volume`: IntegerField
- `difficulty`: IntegerField
- `intent`: CharField (choices: informational, navigational, commercial, transactional)
- `cluster`: ForeignKey to Clusters (optional)
- `status`: CharField (choices: active, pending, archived)
#### Clusters
**File**: `modules/planner/models.py`
**Table**: `igny8_clusters`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `name`: CharField (unique)
- `description`: TextField
- `keywords_count`: IntegerField
- `volume`: IntegerField
- `mapped_pages`: IntegerField
- `status`: CharField
#### ContentIdeas
**File**: `modules/planner/models.py`
**Table**: `igny8_content_ideas`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `idea_title`: CharField
- `description`: TextField
- `content_structure`: CharField (choices: cluster_hub, landing_page, pillar_page, supporting_page)
- `content_type`: CharField (choices: blog_post, article, guide, tutorial)
- `target_keywords`: CharField (comma-separated, legacy)
- `keyword_objects`: ManyToManyField to Keywords
- `keyword_cluster`: ForeignKey to Clusters
- `status`: CharField (choices: new, scheduled, published)
- `estimated_word_count`: IntegerField
### Writer Models
#### Tasks
**File**: `modules/writer/models.py`
**Table**: `igny8_tasks`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `title`: CharField
- `description`: TextField
- `keywords`: CharField (comma-separated, legacy)
- `keyword_objects`: ManyToManyField to Keywords
- `cluster`: ForeignKey to Clusters
- `idea`: ForeignKey to ContentIdeas
- `content_structure`: CharField
- `content_type`: CharField
- `status`: CharField (choices: queued, in_progress, draft, review, published, completed)
- `content`: TextField (generated content)
- `word_count`: IntegerField
- `meta_title`: CharField
- `meta_description`: TextField
- `assigned_post_id`: IntegerField (WordPress post ID)
- `post_url`: URLField
#### Content
**File**: `modules/writer/models.py`
**Table**: `igny8_content`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `task`: OneToOneField to Tasks
- `html_content`: TextField
- `word_count`: IntegerField
- `metadata`: JSONField
**Methods**:
- `save()`: Automatically sets account, site, sector from task
#### Images
**File**: `modules/writer/models.py`
**Table**: `igny8_images`
**Fields**:
- Inherits from `SiteSectorBaseModel`
- `task`: ForeignKey to Tasks
- `image_type`: CharField (choices: featured, desktop, mobile, in_article)
- `image_url`: URLField
- `image_path`: CharField (local path)
- `prompt`: TextField
- `status`: CharField
- `position`: IntegerField
**Methods**:
- `save()`: Automatically sets account, site, sector from task
### System Models
#### AIPrompt
**File**: `modules/system/models.py`
**Table**: `igny8_ai_prompts`
**Fields**:
- Inherits from `AccountBaseModel`
- `prompt_type`: CharField (choices: clustering, ideas, content_generation, image_prompt_extraction, image_prompt_template, negative_prompt)
- `prompt_value`: TextField
- `default_prompt`: TextField
- `is_active`: BooleanField
**Unique Constraint**: `(account, prompt_type)`
#### IntegrationSettings
**File**: `modules/system/models.py`
**Table**: `igny8_integration_settings`
**Fields**:
- Inherits from `AccountBaseModel`
- `integration_type`: CharField (choices: openai, runware, gsc, image_generation)
- `config`: JSONField (API keys, settings, etc.)
- `is_active`: BooleanField
**Unique Constraint**: `(account, integration_type)`
#### AuthorProfile
**File**: `modules/system/models.py`
**Table**: `igny8_author_profiles`
**Fields**:
- Inherits from `AccountBaseModel`
- `name`: CharField
- `description`: TextField
- `tone`: CharField
- `language`: CharField
- `structure_template`: JSONField
- `is_active`: BooleanField
#### Strategy
**File**: `modules/system/models.py`
**Table**: `igny8_strategies`
**Fields**:
- Inherits from `AccountBaseModel`
- `name`: CharField
- `description`: TextField
- `sector`: ForeignKey to Sector (optional)
- `prompt_types`: JSONField
- `section_logic`: JSONField
- `is_active`: BooleanField
---
## ViewSets
### Base ViewSets
#### AccountModelViewSet
**File**: `api/base.py`
**Purpose**: Base ViewSet with automatic account filtering.
**Methods**:
- `get_queryset()`: Filters queryset by `request.account` (with admin/developer override)
- `perform_create()`: Sets account on created objects
- `get_serializer_context()`: Adds account to serializer context
**Access Control**:
- Admin/Developer users: Bypass account filtering
- System account users: Bypass account filtering
- Regular users: Only see data from their account
#### SiteSectorModelViewSet
**File**: `api/base.py`
**Purpose**: Base ViewSet with site/sector filtering and access control.
**Inherits**: `AccountModelViewSet`
**Methods**:
- `get_queryset()`: Filters by account, accessible sites, and optional site_id/sector_id
- `perform_create()`: Validates site access and sector-site relationship
- `get_serializer_context()`: Adds accessible sites and sectors to context
**Access Control**:
- Developers: All active sites
- System account users: All active sites
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites granted via `SiteUserAccess`
### Planner ViewSets
#### KeywordViewSet
**File**: `modules/planner/views.py`
**Inherits**: `SiteSectorModelViewSet`
**Actions**:
- `list()`: List keywords with filtering
- `create()`: Create keyword
- `retrieve()`: Get keyword details
- `update()`: Update keyword
- `destroy()`: Delete keyword
- `auto_cluster()`: Auto-cluster keywords using AI
- `bulk_delete()`: Bulk delete keywords
- `bulk_update_status()`: Bulk update keyword status
- `export_csv()`: Export keywords to CSV
- `import_csv()`: Import keywords from CSV
**Filtering**:
- Search: `keyword` field
- Filters: `status`, `cluster_id`, `intent`
- Custom: `difficulty_min`, `difficulty_max`, `volume_min`, `volume_max`
- Ordering: `created_at`, `volume`, `difficulty`
#### ClusterViewSet
**File**: `modules/planner/views.py`
**Inherits**: `SiteSectorModelViewSet`
**Actions**:
- `list()`: List clusters
- `create()`: Create cluster
- `retrieve()`: Get cluster details
- `update()`: Update cluster
- `destroy()`: Delete cluster
- `auto_generate_ideas()`: Auto-generate content ideas for clusters
#### ContentIdeasViewSet
**File**: `modules/planner/views.py`
**Inherits**: `SiteSectorModelViewSet`
**Actions**:
- `list()`: List content ideas
- `create()`: Create content idea
- `retrieve()`: Get content idea details
- `update()`: Update content idea
- `destroy()`: Delete content idea
### Writer ViewSets
#### TasksViewSet
**File**: `modules/writer/views.py`
**Inherits**: `SiteSectorModelViewSet`
**Actions**:
- `list()`: List tasks
- `create()`: Create task
- `retrieve()`: Get task details
- `update()`: Update task
- `destroy()`: Delete task
- `auto_generate_content()`: Auto-generate content for tasks
- `auto_generate_images()`: Auto-generate images for tasks
- `bulk_delete()`: Bulk delete tasks
- `bulk_update()`: Bulk update task status
**Filtering**:
- Search: `title`, `keywords`
- Filters: `status`, `cluster_id`, `content_type`, `content_structure`
- Ordering: `title`, `created_at`, `word_count`, `status`
### System ViewSets
#### IntegrationSettingsViewSet
**File**: `modules/system/integration_views.py`
**Inherits**: `viewsets.ViewSet`
**Actions**:
- `list()`: List integrations
- `retrieve()`: Get integration settings
- `update()`: Save integration settings
- `save_post()`: Save integration settings (POST)
- `test_connection()`: Test API connection
- `test_openai()`: Test OpenAI connection
- `test_runware()`: Test Runware connection
- `generate_image()`: Test image generation
- `task_progress()`: Get Celery task progress
#### AIPromptViewSet
**File**: `modules/system/views.py`
**Inherits**: `AccountModelViewSet`
**Actions**:
- `list()`: List prompts
- `create()`: Create prompt
- `retrieve()`: Get prompt details
- `update()`: Update prompt
- `destroy()`: Delete prompt
- `reset_to_default()`: Reset prompt to default value
#### AuthorProfileViewSet
**File**: `modules/system/views.py`
**Inherits**: `AccountModelViewSet`
**Actions**:
- `list()`: List author profiles
- `create()`: Create author profile
- `retrieve()`: Get author profile details
- `update()`: Update author profile
- `destroy()`: Delete author profile
---
## Serializers
### Planner Serializers
#### KeywordSerializer
**File**: `modules/planner/serializers.py`
**Fields**: All Keyword model fields
**Validation**: Validates keyword uniqueness, cluster belongs to same sector
#### ClusterSerializer
**File**: `modules/planner/cluster_serializers.py`
**Fields**: All Cluster model fields
**Read-Only Fields**: `keywords_count`, `volume` (calculated)
#### ContentIdeasSerializer
**File**: `modules/planner/serializers.py`
**Fields**: All ContentIdeas model fields
### Writer Serializers
#### TasksSerializer
**File**: `modules/writer/serializers.py`
**Fields**: All Tasks model fields
#### ContentSerializer
**File**: `modules/writer/serializers.py`
**Fields**: All Content model fields
#### ImagesSerializer
**File**: `modules/writer/serializers.py`
**Fields**: All Images model fields
### System Serializers
#### AIPromptSerializer
**File**: `modules/system/serializers.py`
**Fields**: All AIPrompt model fields
#### IntegrationSettingsSerializer
**File**: `modules/system/serializers.py`
**Fields**: All IntegrationSettings model fields
---
## Celery Tasks
### Planner Tasks
#### auto_cluster_keywords_task
**File**: `modules/planner/tasks.py`
**Purpose**: Auto-cluster keywords using AI.
**Parameters**:
- `keyword_ids`: List of keyword IDs
- `account_id`: Account ID
- `site_id`: Site ID
- `sector_id`: Sector ID
**Progress Tracking**: Updates progress with request_steps and response_steps
**Calls**: `_auto_cluster_keywords_core()`
#### auto_generate_ideas_task
**File**: `modules/planner/tasks.py`
**Purpose**: Auto-generate content ideas for clusters.
**Parameters**:
- `cluster_ids`: List of cluster IDs
- `account_id`: Account ID
**Progress Tracking**: Updates progress for each cluster
**Calls**: `_generate_single_idea_core()` for each cluster
### Writer Tasks
#### auto_generate_content_task
**File**: `modules/writer/tasks.py`
**Purpose**: Auto-generate content for tasks.
**Parameters**:
- `task_ids`: List of task IDs
- `account_id`: Account ID
**Progress Tracking**: Updates progress for each task
**Calls**: `AIProcessor.generate_content()`
#### auto_generate_images_task
**File**: `modules/writer/tasks.py`
**Purpose**: Auto-generate images for tasks.
**Parameters**:
- `task_ids`: List of task IDs
- `account_id`: Account ID
**Progress Tracking**: Updates progress for each task
**Calls**: `AIProcessor.extract_image_prompts()` and `AIProcessor.generate_image()`
---
## API Endpoints
### Base URL
`/api/v1/`
### Planner Endpoints
- `GET /api/v1/planner/keywords/` - List keywords
- `POST /api/v1/planner/keywords/` - Create keyword
- `GET /api/v1/planner/keywords/{id}/` - Get keyword
- `PUT /api/v1/planner/keywords/{id}/` - Update keyword
- `DELETE /api/v1/planner/keywords/{id}/` - Delete keyword
- `POST /api/v1/planner/keywords/auto_cluster/` - Auto-cluster keywords
- `POST /api/v1/planner/keywords/bulk_delete/` - Bulk delete keywords
- `POST /api/v1/planner/keywords/bulk_update_status/` - Bulk update status
- `GET /api/v1/planner/keywords/export_csv/` - Export keywords
- `POST /api/v1/planner/keywords/import_csv/` - Import keywords
- `GET /api/v1/planner/clusters/` - List clusters
- `POST /api/v1/planner/clusters/auto_generate_ideas/` - Auto-generate ideas
- `GET /api/v1/planner/ideas/` - List content ideas
### Writer Endpoints
- `GET /api/v1/writer/tasks/` - List tasks
- `POST /api/v1/writer/tasks/auto_generate_content/` - Auto-generate content
- `POST /api/v1/writer/tasks/auto_generate_images/` - Auto-generate images
### System Endpoints
- `GET /api/v1/system/settings/integrations/{pk}/` - Get integration settings
- `PUT /api/v1/system/settings/integrations/{pk}/` - Save integration settings
- `POST /api/v1/system/settings/integrations/{pk}/test_openai/` - Test OpenAI
- `POST /api/v1/system/settings/integrations/{pk}/test_runware/` - Test Runware
- `POST /api/v1/system/settings/integrations/{pk}/generate_image/` - Test image generation
- `GET /api/v1/system/settings/task_progress/{task_id}/` - Get task progress
---
## Base Classes
### AccountModelViewSet
**File**: `api/base.py`
**Purpose**: Base ViewSet with automatic account filtering.
**Features**:
- Automatic account filtering
- Admin/Developer override
- Account context in serializers
### SiteSectorModelViewSet
**File**: `api/base.py`
**Purpose**: Base ViewSet with site/sector filtering.
**Features**:
- Account filtering (inherited)
- Site access control
- Sector validation
- Accessible sites/sectors in serializer context
---
## Middleware
### AccountContextMiddleware
**File**: `middleware/account.py`
**Purpose**: Sets `request.account` from JWT token.
**Functionality**:
- Extracts account ID from JWT token
- Loads Account object
- Sets `request.account`
### ResourceTrackerMiddleware
**File**: `middleware/resource_tracker.py`
**Purpose**: Tracks API request metrics.
**Functionality**:
- Tracks CPU, memory, I/O usage
- Stores metrics in cache
- Provides metrics endpoint
---
## Utilities
### AIProcessor
**File**: `utils/ai_processor.py`
**Purpose**: Unified AI interface for all AI operations.
**Methods**:
- `cluster_keywords()`: Cluster keywords using AI
- `generate_ideas()`: Generate content ideas
- `generate_content()`: Generate text content
- `extract_image_prompts()`: Extract image prompts from content
- `generate_image()`: Generate images using OpenAI DALL-E or Runware
**See**: AI Functions documentation for complete details
### Content Normalizer
**File**: `utils/content_normalizer.py`
**Purpose**: Content processing utilities.
**Functions**:
- `_extract_body_content()`: Extract body content from HTML
---
## Modules
### Planner Module
**Purpose**: Keyword management and content planning.
**Models**: Keywords, Clusters, ContentIdeas
**ViewSets**: KeywordViewSet, ClusterViewSet, ContentIdeasViewSet
**Tasks**: auto_cluster_keywords_task, auto_generate_ideas_task
### Writer Module
**Purpose**: Content generation and management.
**Models**: Tasks, Content, Images
**ViewSets**: TasksViewSet
**Tasks**: auto_generate_content_task, auto_generate_images_task
### System Module
**Purpose**: System settings, prompts, and integrations.
**Models**: AIPrompt, IntegrationSettings, AuthorProfile, Strategy
**ViewSets**: AIPromptViewSet, AuthorProfileViewSet, IntegrationSettingsViewSet
**Utilities**: Default prompts, prompt loading
### Billing Module
**Purpose**: Credits, transactions, and usage tracking.
**Models**: CreditTransaction, UsageLog
**ViewSets**: CreditTransactionViewSet, UsageLogViewSet
**Services**: CreditService
---
## Summary
The IGNY8 backend is built on:
1. **Django + DRF**: Robust web framework with RESTful API
2. **Multi-Tenancy**: Complete account isolation with automatic filtering
3. **Modular Architecture**: Clear module boundaries with shared utilities
4. **Celery Integration**: Asynchronous task processing for long-running operations
5. **Base ViewSets**: Consistent access control and filtering
6. **AI Integration**: Unified AIProcessor for all AI operations
7. **Progress Tracking**: Real-time progress updates for Celery tasks
8. **Account/Site/Sector Hierarchy**: Hierarchical data organization
This architecture ensures scalability, maintainability, and extensibility while providing a robust API for the frontend.

View File

@@ -0,0 +1,739 @@
# IGNY8 Account, User, Plan, Sites, Credits Documentation
**Version:** 1.0
**Last Updated:** 2025-01-XX
**Purpose:** Complete documentation of multi-tenancy architecture, access control, plans, credits, and related systems.
---
## Table of Contents
1. [Overview](#overview)
2. [Account Model](#account-model)
3. [User Model](#user-model)
4. [Plan Model](#plan-model)
5. [Subscription Model](#subscription-model)
6. [Site Model](#site-model)
7. [Sector Model](#sector-model)
8. [Industry & IndustrySector Models](#industry--industrysector-models)
9. [Access Control](#access-control)
10. [Credits System](#credits-system)
11. [Plan Limits](#plan-limits)
12. [Multi-Tenancy Architecture](#multi-tenancy-architecture)
---
## Overview
The IGNY8 platform uses a multi-account architecture with the following core entities:
- **Account**: Top-level organization/workspace
- **User**: Individual user accounts with roles
- **Plan**: Subscription plan templates with limits
- **Subscription**: Active subscription linking Account to Plan
- **Site**: Workspace within an Account (1-N relationship)
- **Sector**: Content category within a Site (1-5 per site)
- **Industry**: Global industry templates
- **IndustrySector**: Industry sector templates
### Key Relationships
```
Account (1) ──< (N) User
Account (1) ──< (1) Subscription ──> (1) Plan
Account (1) ──< (N) Site
Site (1) ──< (1-5) Sector
Industry (1) ──< (N) IndustrySector
Site (1) ──> (1) Industry (optional)
Sector (1) ──> (1) IndustrySector (optional, template reference)
```
---
## Account Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_accounts`
### Fields
- `id`: Primary key
- `name`: CharField - Account name
- `slug`: SlugField - Unique slug identifier
- `owner`: ForeignKey to User - Account owner
- `stripe_customer_id`: CharField - Stripe customer ID (optional)
- `plan`: ForeignKey to Plan - Current subscription plan
- `credits`: IntegerField - Current credit balance (default: 0)
- `status`: CharField - Account status (choices: active, suspended, trial, cancelled)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Status Values
- `active`: Normal operation
- `suspended`: Access temporarily revoked
- `trial`: Limited trial access
- `cancelled`: Account terminated
### Methods
#### is_system_account()
**Returns**: `True` if account is a system account (aws-admin, default-account, default)
**Purpose**: System accounts bypass all filtering restrictions
### Relationships
- **One-to-Many**: Users (via `user.account`)
- **One-to-One**: Subscription (via `account.subscription`)
- **One-to-Many**: Sites (via `site.account`)
---
## User Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_users`
**Inherits**: `AbstractUser` (Django's built-in user model)
### Fields
- `id`: Primary key (inherited from AbstractUser)
- `email`: EmailField - Unique, USERNAME_FIELD
- `username`: CharField - Username
- `password`: Hashed password (inherited)
- `account`: ForeignKey to Account - User's account (null=True, blank=True)
- `role`: CharField - User role (choices: developer, owner, admin, editor, viewer, system_bot)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Role Hierarchy (from highest to lowest)
1. **developer**: Full system access, bypasses all restrictions
2. **owner**: Full access to account, can manage users and billing
3. **admin**: Admin access to account, can manage content and users
4. **editor**: Can edit content, manage clusters/tasks
5. **viewer**: Read-only access
6. **system_bot**: System automation user
### Methods
#### has_role(*roles)
**Parameters**: `*roles` - Variable number of role strings
**Returns**: `True` if user has any of the specified roles
#### is_owner_or_admin()
**Returns**: `True` if role is owner or admin
#### is_developer()
**Returns**: `True` if role is developer or is_superuser
#### is_admin_or_developer()
**Returns**: `True` if role is admin or developer (bypasses restrictions)
**Purpose**: Admin/Developer users bypass account/site/sector filtering
#### is_system_account_user()
**Returns**: `True` if user belongs to system account
**Purpose**: System account users bypass all filtering restrictions
#### get_accessible_sites()
**Returns**: QuerySet of sites user can access
**Access Control Logic**:
- **System Account Users**: All active sites across all accounts
- **Developers**: All active sites across all accounts
- **Owners/Admins**: All sites in their account
- **Editors/Viewers**: Only sites explicitly granted via `SiteUserAccess`
### Relationships
- **Many-to-One**: Account (via `user.account`)
- **Many-to-Many**: Sites (via `SiteUserAccess`)
---
## Plan Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_plans`
### Fields
#### Plan Information
- `id`: Primary key
- `name`: CharField - Plan name (e.g., "Free", "Starter", "Growth")
- `slug`: SlugField - Unique slug identifier
- `price`: DecimalField - Monthly price
- `billing_cycle`: CharField - Billing cycle (choices: monthly, annual)
- `features`: JSONField - Array of feature strings (e.g., ['ai_writer', 'image_gen', 'auto_publish'])
- `is_active`: BooleanField - Whether plan is available for subscription
#### User/Site/Scope Limits
- `max_users`: IntegerField - Total users allowed per account (default: 1, min: 1)
- `max_sites`: IntegerField - Maximum sites allowed (default: 1, min: 1)
- `max_industries`: IntegerField - Optional limit for industries/sectors (nullable)
- `max_author_profiles`: IntegerField - Limit for saved writing styles (default: 5)
#### Planner Limits
- `max_keywords`: IntegerField - Total keywords allowed globally (default: 1000)
- `max_clusters`: IntegerField - Total clusters allowed globally (default: 100)
- `max_content_ideas`: IntegerField - Total content ideas allowed globally (default: 300)
- `daily_cluster_limit`: IntegerField - Max clusters per day (default: 10)
- `daily_keyword_import_limit`: IntegerField - Seed keywords import limit per day (default: 100)
- `monthly_cluster_ai_credits`: IntegerField - AI credits for clustering (default: 50)
#### Writer Limits
- `daily_content_tasks`: IntegerField - Max content tasks per day (default: 10)
- `daily_ai_requests`: IntegerField - Total AI executions per day (default: 50)
- `monthly_word_count_limit`: IntegerField - Monthly word limit for generated content (default: 50000)
- `monthly_content_ai_credits`: IntegerField - AI credit pool for content generation (default: 200)
#### Image Generation Limits
- `monthly_image_count`: IntegerField - Max images per month (default: 100)
- `daily_image_generation_limit`: IntegerField - Max images per day (default: 25)
- `monthly_image_ai_credits`: IntegerField - AI credit pool for images (default: 100)
- `max_images_per_task`: IntegerField - Max images per content task (default: 4, min: 1)
- `image_model_choices`: JSONField - Allowed image models (e.g., ['dalle3', 'hidream'])
#### AI Request Controls
- `daily_ai_request_limit`: IntegerField - Global daily AI request cap (default: 100)
- `monthly_ai_credit_limit`: IntegerField - Unified credit ceiling per month (default: 500)
#### Billing & Credits
- `included_credits`: IntegerField - Monthly credits included (default: 0)
- `extra_credit_price`: DecimalField - Price per additional credit (default: 0.01)
- `allow_credit_topup`: BooleanField - Can user purchase more credits? (default: True)
- `auto_credit_topup_threshold`: IntegerField - Auto top-up trigger point (nullable)
- `auto_credit_topup_amount`: IntegerField - Credits to auto-buy (nullable)
#### Stripe Integration
- `stripe_product_id`: CharField - Stripe product ID (nullable)
- `stripe_price_id`: CharField - Stripe price ID (nullable)
#### Legacy Fields
- `credits_per_month`: IntegerField - DEPRECATED: Use included_credits instead (default: 0)
### Methods
#### clean()
**Purpose**: Validates plan limits
**Validations**:
- `max_sites` must be >= 1
- `included_credits` must be >= 0
#### get_effective_credits_per_month()
**Returns**: `included_credits` if set, otherwise `credits_per_month` (backward compatibility)
### Relationships
- **One-to-Many**: Accounts (via `account.plan`)
### Plan Limits Enforcement
**Current Status**: Plan limits are defined but **not currently enforced** in AI functions.
**Future Implementation**: Plan limits should be checked before allowing operations.
---
## Subscription Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_subscriptions`
### Fields
- `id`: Primary key
- `account`: OneToOneField to Account - Account subscription
- `stripe_subscription_id`: CharField - Unique Stripe subscription ID
- `status`: CharField - Subscription status (choices: active, past_due, canceled, trialing)
- `current_period_start`: DateTimeField - Current billing period start
- `current_period_end`: DateTimeField - Current billing period end
- `cancel_at_period_end`: BooleanField - Cancel at period end flag (default: False)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Status Values
- `active`: Subscription is active and paid
- `past_due`: Payment failed, subscription past due
- `canceled`: Subscription canceled
- `trialing`: In trial period
### Methods
### Relationships
- **One-to-One**: Account (via `account.subscription`)
---
## Site Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_sites`
**Inherits**: `AccountBaseModel`
### Fields
- `id`: Primary key
- `account`: ForeignKey to Account - Account this site belongs to
- `name`: CharField - Site name
- `slug`: SlugField - Unique slug per account
- `domain`: URLField - Primary domain URL (optional)
- `description`: TextField - Site description (optional)
- `industry`: ForeignKey to Industry - Industry this site belongs to (optional)
- `is_active`: BooleanField - Whether site is active (default: True)
- `status`: CharField - Site status (choices: active, inactive, suspended, default: active)
- `wp_url`: URLField - WordPress site URL (optional, for WordPress integration)
- `wp_username`: CharField - WordPress username (optional)
- `wp_app_password`: CharField - WordPress app password (optional)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Unique Constraint
- `(account, slug)` - Slug unique per account
### Methods
#### get_active_sectors_count()
**Returns**: Count of active sectors for this site
#### can_add_sector()
**Returns**: `True` if site can add another sector (max 5 sectors per site)
### Relationships
- **Many-to-One**: Account (via `site.account`)
- **Many-to-One**: Industry (via `site.industry`, optional)
- **One-to-Many**: Sectors (via `sector.site`)
- **Many-to-Many**: Users (via `SiteUserAccess`)
### Site Activation
**Important**: Multiple sites can be active simultaneously. The previous restriction of "only 1 site can be active at one time" has been removed.
---
## Sector Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_sectors`
**Inherits**: `AccountBaseModel`
### Fields
- `id`: Primary key
- `account`: ForeignKey to Account - Account this sector belongs to
- `site`: ForeignKey to Site - Site this sector belongs to
- `industry_sector`: ForeignKey to IndustrySector - Reference to industry sector template (optional)
- `name`: CharField - Sector name
- `slug`: SlugField - Unique slug per site
- `description`: TextField - Sector description (optional)
- `is_active`: BooleanField - Whether sector is active (default: True)
- `status`: CharField - Sector status (choices: active, inactive, default: active)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Unique Constraint
- `(site, slug)` - Slug unique per site
### Validation
**On Save**:
1. Automatically sets `account` from `site.account`
2. Validates that `industry_sector.industry` matches `site.industry` (if both set)
3. Validates sector limit: Maximum 5 active sectors per site
### Methods
#### industry (property)
**Returns**: Industry for this sector (from `industry_sector.industry` if set)
### Relationships
- **Many-to-One**: Account (via `sector.account`)
- **Many-to-One**: Site (via `sector.site`)
- **Many-to-One**: IndustrySector (via `sector.industry_sector`, optional, template reference)
- **One-to-Many**: Keywords, Clusters, ContentIdeas, Tasks (via their `sector` field)
---
## Industry & IndustrySector Models
### Industry Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_industries`
**Purpose**: Global industry templates.
### Fields
- `id`: Primary key
- `name`: CharField - Industry name (unique)
- `slug`: SlugField - Unique slug identifier
- `description`: TextField - Industry description (optional)
- `is_active`: BooleanField - Whether industry is active (default: True)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Relationships
- **One-to-Many**: Sites (via `site.industry`)
- **One-to-Many**: IndustrySectors (via `industry_sector.industry`)
### IndustrySector Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_industry_sectors`
**Purpose**: Sector templates within industries.
### Fields
- `id`: Primary key
- `industry`: ForeignKey to Industry - Industry this sector belongs to
- `name`: CharField - Sector name
- `slug`: SlugField - Unique slug per industry
- `description`: TextField - Sector description (optional)
- `suggested_keywords`: JSONField - List of suggested keywords for this sector template
- `is_active`: BooleanField - Whether sector is active (default: True)
- `created_at`: DateTimeField - Creation timestamp
- `updated_at`: DateTimeField - Last update timestamp
### Unique Constraint
- `(industry, slug)` - Slug unique per industry
### Relationships
- **Many-to-One**: Industry (via `industry_sector.industry`)
- **One-to-Many**: Sectors (via `sector.industry_sector`, template reference)
- **One-to-Many**: SeedKeywords (via `seed_keyword.sector`)
---
## Access Control
### Account Isolation
**Principle**: All data is isolated by account.
**Implementation**:
- All models inherit `AccountBaseModel` (has `account` ForeignKey)
- All ViewSets inherit `AccountModelViewSet` (filters by `request.account`)
- Middleware sets `request.account` from JWT token
**Access Control**:
- **Admin/Developer users**: Bypass account filtering (see all accounts)
- **System account users**: Bypass account filtering (see all accounts)
- **Regular users**: Only see data from their account
### Site Access Control
**Principle**: Users can only access sites they have permission to access.
**Implementation**:
- `User.get_accessible_sites()` returns sites user can access
- ViewSets filter by accessible sites
- `SiteUserAccess` model controls explicit site access for editors/viewers
**Access Levels**:
- **System Account Users**: All active sites across all accounts
- **Developers**: All active sites across all accounts
- **Owners/Admins**: All sites in their account
- **Editors/Viewers**: Only sites explicitly granted via `SiteUserAccess`
### SiteUserAccess Model
**File**: `backend/igny8_core/auth/models.py`
**Table**: `igny8_site_user_access`
**Purpose**: Many-to-many relationship between Users and Sites for explicit access control.
### Fields
- `id`: Primary key
- `user`: ForeignKey to User
- `site`: ForeignKey to Site
- `granted_at`: DateTimeField - When access was granted
- `granted_by`: ForeignKey to User - Who granted access (optional)
### Unique Constraint
- `(user, site)` - One access record per user-site pair
### Usage
- Owners and Admins have automatic access to all sites in their account
- Editors and Viewers require explicit `SiteUserAccess` records
- System account users and Developers bypass this check
### Role-Based Access Control (RBAC)
**Role Hierarchy**:
1. **developer**: Full system access, bypasses all restrictions
2. **owner**: Full account access, can manage users and billing
3. **admin**: Account admin access, can manage content and users
4. **editor**: Content editing access, can manage clusters/tasks
5. **viewer**: Read-only access
**Permission Checks**:
- `user.has_role(*roles)`: Check if user has any of the specified roles
- `user.is_owner_or_admin()`: Check if user is owner or admin
- `user.is_developer()`: Check if user is developer
- `user.is_admin_or_developer()`: Check if user is admin or developer (bypasses restrictions)
---
## Credits System
### Account Credits
**Field**: `Account.credits` (IntegerField, default: 0)
**Purpose**: Current credit balance for the account.
**Usage**: Credits are deducted for AI operations and added through purchases or subscriptions.
### Credit Models
#### CreditTransaction
**File**: `backend/igny8_core/modules/billing/models.py`
**Table**: `igny8_credit_transactions`
**Purpose**: Track all credit transactions (additions, deductions).
### Fields
- `id`: Primary key
- `account`: ForeignKey to Account
- `transaction_type`: CharField - Type of transaction (choices: purchase, subscription, refund, deduction, adjustment)
- `amount`: IntegerField - Positive for additions, negative for deductions
- `balance_after`: IntegerField - Credit balance after this transaction
- `description`: CharField - Transaction description
- `metadata`: JSONField - Additional context (AI call details, etc.)
- `created_at`: DateTimeField - Creation timestamp
#### CreditUsageLog
**File**: `backend/igny8_core/modules/billing/models.py`
**Table**: `igny8_credit_usage_logs`
**Purpose**: Detailed log of credit usage per AI operation.
### Fields
- `id`: Primary key
- `account`: ForeignKey to Account
- `operation_type`: CharField - Type of operation (choices: clustering, ideas, content, images, reparse)
- `credits_used`: IntegerField - Number of credits used
- `cost_usd`: DecimalField - Cost in USD (optional)
- `model_used`: CharField - AI model used (optional)
- `tokens_input`: IntegerField - Input tokens (optional)
- `tokens_output`: IntegerField - Output tokens (optional)
- `related_object_type`: CharField - Related object type (optional)
- `related_object_id`: IntegerField - Related object ID (optional)
- `metadata`: JSONField - Additional metadata
- `created_at`: DateTimeField - Creation timestamp
### CreditService
**File**: `backend/igny8_core/modules/billing/services.py`
**Purpose**: Service for managing credits.
#### Methods
##### check_credits(account, required_credits)
**Purpose**: Check if account has enough credits.
**Raises**: `InsufficientCreditsError` if account doesn't have enough credits
##### deduct_credits(account, amount, operation_type, description, ...)
**Purpose**: Deduct credits and log transaction.
**Parameters**:
- `account`: Account instance
- `amount`: Number of credits to deduct
- `operation_type`: Type of operation
- `description`: Description of the transaction
- `metadata`: Optional metadata dict
- `cost_usd`: Optional cost in USD
- `model_used`: Optional AI model used
- `tokens_input`: Optional input tokens
- `tokens_output`: Optional output tokens
- `related_object_type`: Optional related object type
- `related_object_id`: Optional related object ID
**Returns**: New credit balance
**Creates**:
- `CreditTransaction` record
- `CreditUsageLog` record
##### add_credits(account, amount, transaction_type, description, ...)
**Purpose**: Add credits (purchase, subscription, etc.).
**Parameters**:
- `account`: Account instance
- `amount`: Number of credits to add
- `transaction_type`: Type of transaction
- `description`: Description of the transaction
- `metadata`: Optional metadata dict
**Returns**: New credit balance
**Creates**: `CreditTransaction` record
##### calculate_credits_for_operation(operation_type, **kwargs)
**Purpose**: Calculate credits needed for an operation.
**Parameters**:
- `operation_type`: Type of operation
- `**kwargs`: Operation-specific parameters
**Returns**: Number of credits required
**Raises**: `CreditCalculationError` if calculation fails
**Credit Costs**:
- `clustering`: 1 credit per 30 keywords
- `ideas`: 1 credit per idea
- `content`: 3 credits per content piece
- `images`: 1 credit per image
- `reparse`: 1 credit per reparse
### Credit Integration
**Current Status**: Credits system is implemented but **not currently enforced** in AI functions.
**Future Implementation**: Credits should be checked and deducted before allowing AI operations.
---
## Plan Limits
### Planner Limits
- `max_keywords`: Total keywords allowed (not enforced)
- `max_clusters`: Total clusters allowed (not enforced)
- `max_content_ideas`: Total content ideas allowed (not enforced)
- `daily_cluster_limit`: Max clusters per day (not enforced)
- `monthly_cluster_ai_credits`: AI credits for clustering (not enforced)
### Writer Limits
- `daily_content_tasks`: Max content tasks per day (not enforced)
- `daily_ai_requests`: Total AI executions per day (not enforced)
- `monthly_word_count_limit`: Monthly word limit (not enforced)
- `monthly_content_ai_credits`: AI credits for content (not enforced)
### Image Limits
- `monthly_image_count`: Max images per month (not enforced)
- `daily_image_generation_limit`: Max images per day (not enforced)
- `monthly_image_ai_credits`: AI credits for images (not enforced)
- `max_images_per_task`: Max images per task (not enforced)
- `image_model_choices`: Allowed image models (not enforced)
### Feature Flags
- `features`: JSON array of enabled features (e.g., `['ai_writer', 'image_gen', 'auto_publish']`)
- **Not enforced**: Feature flags are not checked before allowing AI operations
### Plan Limits Enforcement
**Current Status**: Plan limits are defined but **not currently enforced** in AI functions.
**Future Implementation**: Plan limits should be checked before allowing operations.
---
## Multi-Tenancy Architecture
### Account Isolation
**Principle**: All data is isolated by account.
**Implementation**:
1. **Model Level**: All models inherit `AccountBaseModel` (has `account` ForeignKey)
2. **ViewSet Level**: All ViewSets inherit `AccountModelViewSet` (filters by `request.account`)
3. **Middleware Level**: `AccountContextMiddleware` sets `request.account` from JWT token
**Access Control**:
- Admin/Developer users: Bypass account filtering (see all accounts)
- System account users: Bypass account filtering (see all accounts)
- Regular users: Only see data from their account
### Site/Sector Hierarchy
**Structure**:
```
Account (1) ──< (N) Site
Site (1) ──< (1-5) Sector
Sector (1) ──< (N) Keywords, Clusters, ContentIdeas, Tasks
```
**Implementation**:
- Models inherit `SiteSectorBaseModel` (has `site` and `sector` ForeignKeys)
- ViewSets inherit `SiteSectorModelViewSet` (filters by accessible sites)
- User access control via `User.get_accessible_sites()`
**Site Access Control**:
- System account users: All active sites
- Developers: All active sites
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites granted via `SiteUserAccess`
### Data Isolation Flow
1. **Request Arrives**: JWT token contains account ID
2. **Middleware**: `AccountContextMiddleware` extracts account ID and sets `request.account`
3. **ViewSet**: `AccountModelViewSet.get_queryset()` filters by `request.account`
4. **Database**: All queries automatically filtered by account
**Override**: Admin/Developer users bypass account filtering
---
## Summary
The IGNY8 multi-tenancy architecture provides:
1. **Complete Account Isolation**: All data isolated by account with automatic filtering
2. **Site/Sector Hierarchy**: Hierarchical organization with access control
3. **Role-Based Access Control**: Granular permissions based on user roles
4. **Plan-Based Limits**: Comprehensive plan limits (defined but not enforced)
5. **Credits System**: Credit tracking and deduction system (implemented but not enforced)
6. **Flexible Site Access**: Multiple sites can be active simultaneously
7. **Industry Templates**: Global industry and sector templates
This architecture ensures data security, scalability, and flexibility while providing a solid foundation for subscription management and billing.

File diff suppressed because it is too large Load Diff

274
docs/07-AI-FRAMEWORK.md Normal file
View File

@@ -0,0 +1,274 @@
# IGNY8 AI Framework Documentation
**Version:** 1.0
**Last Updated:** 2025-01-XX
**Purpose:** Complete documentation of the unified AI framework architecture.
---
## Overview
The IGNY8 AI Framework provides a unified, consistent architecture for all AI functions. It eliminates code duplication, standardizes progress tracking, and provides a single interface for all AI operations.
### Key Benefits
- **90% Code Reduction**: Functions are now ~100 lines instead of ~600
- **Consistent UX**: All functions use the same progress modal and tracking
- **Unified Logging**: Single `AITaskLog` table for all AI operations
- **Easy Extension**: Add new functions by creating one class
- **Better Debugging**: Detailed step-by-step tracking for all operations
---
## Architecture
### Directory Structure
```
igny8_core/ai/
├── __init__.py # Auto-registers all functions
├── apps.py # Django app configuration
├── admin.py # Admin interface for AITaskLog
├── base.py # BaseAIFunction abstract class
├── engine.py # AIEngine orchestrator
├── processor.py # AIProcessor wrapper
├── registry.py # Function registry
├── tracker.py # StepTracker, ProgressTracker, CostTracker
├── tasks.py # Unified Celery task entrypoint
├── types.py # Shared dataclasses
├── models.py # AITaskLog model
└── functions/ # Function implementations
├── __init__.py
└── auto_cluster.py # Auto cluster function
```
---
## Core Components
### 1. BaseAIFunction
Abstract base class that all AI functions inherit from.
**Methods to implement:**
- `get_name()`: Return function name
- `prepare()`: Load and prepare data
- `build_prompt()`: Build AI prompt
- `parse_response()`: Parse AI response
- `save_output()`: Save results to database
**Optional overrides:**
- `validate()`: Custom validation
- `get_max_items()`: Set item limit
- `get_model()`: Specify AI model
- `get_metadata()`: Function metadata
### 2. AIEngine
Central orchestrator that manages the execution pipeline.
**Phases:**
- INIT (0-10%): Validation & setup
- PREP (10-25%): Data loading & prompt building
- AI_CALL (25-60%): API call to provider
- PARSE (60-80%): Response parsing
- SAVE (80-95%): Database operations
- DONE (95-100%): Finalization
### 3. Function Registry
Dynamic function discovery system.
**Usage:**
```python
from igny8_core.ai.registry import register_function, get_function
# Register function
register_function('auto_cluster', AutoClusterFunction)
# Get function
fn = get_function('auto_cluster')
```
### 4. Unified Celery Task
Single entrypoint for all AI functions.
**Endpoint:** `run_ai_task(function_name, payload, account_id)`
**Example:**
```python
from igny8_core.ai.tasks import run_ai_task
task = run_ai_task.delay(
function_name='auto_cluster',
payload={'ids': [1, 2, 3], 'sector_id': 1},
account_id=1
)
```
---
## Function Implementation Example
### Auto Cluster Function
```python
from igny8_core.ai.base import BaseAIFunction
class AutoClusterFunction(BaseAIFunction):
def get_name(self) -> str:
return 'auto_cluster'
def get_max_items(self) -> int:
return 20
def prepare(self, payload: dict, account=None) -> Dict:
# Load keywords
ids = payload.get('ids', [])
keywords = Keywords.objects.filter(id__in=ids)
return {'keywords': keywords, ...}
def build_prompt(self, data: Dict, account=None) -> str:
# Build clustering prompt
return prompt_template.replace('[IGNY8_KEYWORDS]', keywords_text)
def parse_response(self, response: str, step_tracker=None) -> List[Dict]:
# Parse AI response
return clusters
def save_output(self, parsed, original_data, account, progress_tracker) -> Dict:
# Save clusters to database
return {'clusters_created': 5, 'keywords_updated': 20}
```
---
## API Endpoint Example
### Before (Old): ~300 lines
### After (New): ~50 lines
```python
@action(detail=False, methods=['post'], url_path='auto_cluster')
def auto_cluster(self, request):
from igny8_core.ai.tasks import run_ai_task
account = getattr(request, 'account', None)
account_id = account.id if account else None
payload = {
'ids': request.data.get('ids', []),
'sector_id': request.data.get('sector_id')
}
task = run_ai_task.delay(
function_name='auto_cluster',
payload=payload,
account_id=account_id
)
return Response({
'success': True,
'task_id': str(task.id),
'message': 'Clustering started'
})
```
---
## Progress Tracking
### Unified Progress Endpoint
**URL:** `/api/v1/system/settings/task_progress/<task_id>/`
**Response:**
```json
{
"state": "PROGRESS",
"meta": {
"phase": "AI_CALL",
"percentage": 45,
"message": "Analyzing keyword relationships...",
"request_steps": [...],
"response_steps": [...],
"cost": 0.000123,
"tokens": 1500
}
}
```
### Frontend Integration
All AI functions use the same progress modal:
- Single `useProgressModal` hook
- Unified progress endpoint
- Consistent phase labels
- Step-by-step logs
---
## Database Logging
### AITaskLog Model
Unified logging table for all AI operations.
**Fields:**
- `task_id`: Celery task ID
- `function_name`: Function name
- `account`: Account (required)
- `phase`: Current phase
- `status`: success/error/pending
- `cost`: API cost
- `tokens`: Token usage
- `request_steps`: Request step logs
- `response_steps`: Response step logs
- `error`: Error message (if any)
---
## Migration Guide
### Migrating Existing Functions
1. Create function class inheriting `BaseAIFunction`
2. Implement required methods
3. Register function in `ai/__init__.py`
4. Update API endpoint to use `run_ai_task`
5. Test and remove old code
### Example Migration
**Old code:**
```python
@action(...)
def auto_cluster(self, request):
# 300 lines of code
```
**New code:**
```python
@action(...)
def auto_cluster(self, request):
# 20 lines using framework
```
---
## Summary
The AI Framework provides:
1. **Unified Architecture**: Single framework for all AI functions
2. **Code Reduction**: 90% less code per function
3. **Consistent UX**: Same progress modal for all functions
4. **Better Debugging**: Detailed step tracking
5. **Easy Extension**: Add functions quickly
6. **Unified Logging**: Single log table
7. **Cost Tracking**: Automatic cost calculation
This architecture ensures maintainability, consistency, and extensibility while dramatically reducing code duplication.

128
docs/AI-ELEMENTS-TABLE.md Normal file
View File

@@ -0,0 +1,128 @@
# AI-Related Elements Across Codebase
Complete table of all AI-related logic, configs, prompts, validations, retries, logging, response parsing, etc.
| Type | Name / Key | File | Function / Class | Line No | Notes |
|------|------------|------|------------------|---------|-------|
| **🧠 AI Core Functions** |
| AI Core | `_auto_cluster_keywords_core` | `backend/igny8_core/modules/planner/tasks.py` | `_auto_cluster_keywords_core()` | 26 | Core clustering logic (legacy, used by old tasks) |
| AI Core | `_generate_single_idea_core` | `backend/igny8_core/modules/planner/tasks.py` | `_generate_single_idea_core()` | 1047 | Core idea generation logic (legacy) |
| AI Core | `auto_generate_content_task` | `backend/igny8_core/modules/writer/tasks.py` | `auto_generate_content_task()` | 27 | Celery task for content generation (legacy) |
| AI Core | `AutoClusterFunction` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction` class | 14 | New framework function for clustering |
| AI Core | `cluster_keywords` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.cluster_keywords()` | 1080 | Legacy clustering method |
| AI Core | `generate_ideas` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.generate_ideas()` | 1280 | Legacy idea generation method |
| AI Core | `generate_content` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.generate_content()` | 446 | Legacy content generation method |
| AI Core | `generate_image` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.generate_image()` | 656 | Image generation (OpenAI DALL-E / Runware) |
| AI Core | `generate_image` | `backend/igny8_core/ai/processor.py` | `AIProcessor.generate_image()` | 61 | Framework wrapper for image generation |
| AI Core | `run_ai_task` | `backend/igny8_core/ai/tasks.py` | `run_ai_task()` | 13 | Unified Celery entrypoint for all AI functions |
| AI Core | `AIEngine.execute` | `backend/igny8_core/ai/engine.py` | `AIEngine.execute()` | 26 | Central orchestrator for all AI functions |
| **🔁 Retry / Model Fallback Logic** |
| Retry | `_call_openai` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._call_openai()` | 125 | Main OpenAI API call method with error handling |
| Retry | `_get_api_key` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_api_key()` | 73 | Gets API key from IntegrationSettings or Django settings (fallback) |
| Retry | `_get_model` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_model()` | 98 | Gets model from IntegrationSettings or Django settings (fallback) |
| Retry | `max_retries=3` | `backend/igny8_core/ai/tasks.py` | `@shared_task(bind=True, max_retries=3)` | 12 | Celery task retry configuration |
| Retry | `max_retries=3` | `backend/igny8_core/modules/writer/tasks.py` | `@shared_task(bind=True, max_retries=3)` | 26 | Content generation task retry configuration |
| Retry | Error handling | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 191-305 | HTTP error handling, JSON parsing errors, timeout handling |
| Retry | Fallback to Django settings | `backend/igny8_core/utils/ai_processor.py` | `_get_api_key()` | 91-95 | Falls back to `OPENAI_API_KEY` or `RUNWARE_API_KEY` from settings |
| Retry | Fallback to default model | `backend/igny8_core/utils/ai_processor.py` | `_get_model()` | 120-123 | Falls back to `DEFAULT_AI_MODEL` from settings (default: 'gpt-4.1') |
| **🧱 Prompt Sources** |
| Prompt | `clustering` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('clustering')` | 10-30 | Hardcoded default clustering prompt template |
| Prompt | `ideas` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('ideas')` | 32-55 | Hardcoded default ideas generation prompt template |
| Prompt | `content_generation` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('content_generation')` | 57-75 | Hardcoded default content generation prompt template |
| Prompt | `image_prompt_extraction` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('image_prompt_extraction')` | 77-98 | Hardcoded default image prompt extraction template |
| Prompt | `image_prompt_template` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('image_prompt_template')` | 100 | Hardcoded default image prompt template |
| Prompt | `negative_prompt` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('negative_prompt')` | 102 | Hardcoded default negative prompt |
| Prompt | `get_prompt_value` | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` | 108 | Retrieves prompt from DB (AIPrompt model) or returns default |
| Prompt | `AIPrompt` model | `backend/igny8_core/modules/system/models.py` | `AIPrompt` class | 13 | Database model storing account-specific prompts |
| Prompt | `prompt_type` | `backend/igny8_core/modules/system/models.py` | `AIPrompt.prompt_type` | 25 | Choices: clustering, ideas, content_generation, image_prompt_extraction, image_prompt_template, negative_prompt |
| Prompt | `build_prompt` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.build_prompt()` | 117 | Builds clustering prompt using `get_prompt_value()` |
| Prompt | `[IGNY8_KEYWORDS]` | `backend/igny8_core/ai/functions/auto_cluster.py` | `build_prompt()` | 131 | Placeholder replaced with keyword list |
| Prompt | `[IGNY8_CLUSTERS]` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('ideas')` | 34 | Placeholder for ideas prompt |
| Prompt | `[IGNY8_IDEA]` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt('content_generation')` | 60 | Placeholder for content generation prompt |
| **⚠️ Validation / Limits** |
| Validation | `validate` | `backend/igny8_core/ai/base.py` | `BaseAIFunction.validate()` | 34 | Base validation: checks for 'ids' array, max_items limit |
| Validation | `validate` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.validate()` | 37 | Custom validation: checks keywords exist, plan limits |
| Validation | `get_max_items` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.get_max_items()` | 34 | Returns 20 (max keywords per cluster operation) |
| Validation | `check_credits` | `backend/igny8_core/modules/billing/services.py` | `CreditService.check_credits()` | 16 | Checks if account has enough credits |
| Validation | Daily cluster limit | `backend/igny8_core/ai/functions/auto_cluster.py` | `validate()` | 59-71 | Checks `plan.daily_cluster_limit` against clusters created today |
| Validation | Max clusters limit | `backend/igny8_core/ai/functions/auto_cluster.py` | `validate()` | 73-79 | Checks `plan.max_clusters` against total clusters |
| Validation | `VALID_OPENAI_IMAGE_MODELS` | `backend/igny8_core/utils/ai_processor.py` | Constant | 34 | Set: {'dall-e-3', 'dall-e-2'} |
| Validation | `VALID_SIZES_BY_MODEL` | `backend/igny8_core/utils/ai_processor.py` | Constant | 41 | Dict mapping models to valid sizes |
| Validation | Model validation | `backend/igny8_core/utils/ai_processor.py` | `generate_image()` | 704-708 | Validates model is in VALID_OPENAI_IMAGE_MODELS |
| Validation | Size validation | `backend/igny8_core/utils/ai_processor.py` | `generate_image()` | 719-724 | Validates size is valid for selected model |
| Validation | Model rate validation | `backend/igny8_core/utils/ai_processor.py` | `_get_model()` | 112 | Validates model is in MODEL_RATES before using |
| **🪵 AI Debug Steps** |
| Debug Step | `addRequestStep` | `backend/igny8_core/ai/tracker.py` | `StepTracker.add_request_step()` | 21 | Adds request phase step (INIT, PREP, SAVE, DONE) |
| Debug Step | `addResponseStep` | `backend/igny8_core/ai/tracker.py` | `StepTracker.add_response_step()` | 45 | Adds response phase step (AI_CALL, PARSE) |
| Debug Step | `request_steps` | `backend/igny8_core/ai/tracker.py` | `StepTracker.request_steps` | 17 | List of request steps |
| Debug Step | `response_steps` | `backend/igny8_core/ai/tracker.py` | `StepTracker.response_steps` | 18 | List of response steps |
| Debug Step | `request_steps` | `backend/igny8_core/modules/planner/tasks.py` | `_auto_cluster_keywords_core()` | 37 | Legacy request steps tracking |
| Debug Step | `response_steps` | `backend/igny8_core/modules/planner/tasks.py` | `_auto_cluster_keywords_core()` | 38 | Legacy response steps tracking |
| Debug Step | `meta['request_steps']` | `backend/igny8_core/modules/system/integration_views.py` | `task_progress()` | 936-937 | Extracts request_steps from Celery task meta |
| Debug Step | `meta['response_steps']` | `backend/igny8_core/modules/system/integration_views.py` | `task_progress()` | 938-939 | Extracts response_steps from Celery task meta |
| Debug Step | `request_steps` | `backend/igny8_core/ai/models.py` | `AITaskLog.request_steps` | 31 | JSONField storing request steps in database |
| Debug Step | `response_steps` | `backend/igny8_core/ai/models.py` | `AITaskLog.response_steps` | 32 | JSONField storing response steps in database |
| Debug Step | `get_meta()` | `backend/igny8_core/ai/tracker.py` | `StepTracker.get_meta()` | 69 | Returns dict with request_steps and response_steps |
| Debug Step | Step injection | `backend/igny8_core/ai/engine.py` | `execute()` | 47, 60, 68, 125, 138, 174 | Adds steps at each phase (INIT, PREP, AI_CALL, PARSE, SAVE, DONE) |
| Debug Step | Step counter | `backend/igny8_core/ai/tracker.py` | `StepTracker.step_counter` | 19 | Auto-increments for sequential step numbers |
| **🧾 Model Config / Selection** |
| Model Config | `default_model` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.__init__()` | 67 | Set from `_get_model()` or defaults to 'gpt-4.1' |
| Model Config | `_get_model` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_model()` | 98 | Gets model from IntegrationSettings.config['model'] or Django settings |
| Model Config | `MODEL_RATES` | `backend/igny8_core/utils/ai_processor.py` | Constant | 19 | Dict: {'gpt-4.1': {...}, 'gpt-4o-mini': {...}, 'gpt-4o': {...}} |
| Model Config | `IMAGE_MODEL_RATES` | `backend/igny8_core/utils/ai_processor.py` | Constant | 26 | Dict: {'dall-e-3': 0.040, 'dall-e-2': 0.020, ...} |
| Model Config | `DEFAULT_AI_MODEL` | `backend/igny8_core/utils/ai_processor.py` | Django setting | 121 | Default: 'gpt-4.1' if not set |
| Model Config | `get_model` | `backend/igny8_core/ai/base.py` | `BaseAIFunction.get_model()` | 70 | Override to specify model (defaults to account's default) |
| Model Config | `model` parameter | `backend/igny8_core/ai/processor.py` | `call()` | 22 | Model can be passed to AIProcessor.call() |
| Model Config | `json_models` | `backend/igny8_core/ai/processor.py` | `call()` | 40 | List: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo-preview'] (supports JSON mode) |
| Model Config | `IntegrationSettings.config['model']` | `backend/igny8_core/modules/system/models.py` | `IntegrationSettings.config` | 56 | JSONField storing model name per account |
| Model Config | Model selection | `backend/igny8_core/ai/engine.py` | `execute()` | 65 | Gets model via `fn.get_model(self.account)` |
| **📦 Request/Response Structuring** |
| Request/Response | `response_format` | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 131 | Optional dict for JSON mode: {'type': 'json_object'} |
| Request/Response | `response_format` | `backend/igny8_core/ai/processor.py` | `call()` | 25 | Optional dict for JSON mode |
| Request/Response | JSON mode auto-enable | `backend/igny8_core/ai/processor.py` | `call()` | 41-42 | Auto-enables JSON mode for json_models if not specified |
| Request/Response | `_extract_json_from_response` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._extract_json_from_response()` | 334 | Extracts JSON from response (handles markdown code blocks, multiline) |
| Request/Response | `extract_json` | `backend/igny8_core/ai/processor.py` | `AIProcessor.extract_json()` | 57 | Wrapper for `_extract_json_from_response()` |
| Request/Response | `parse_response` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.parse_response()` | 158 | Parses AI response into cluster data structure |
| Request/Response | `parse_response` | `backend/igny8_core/ai/base.py` | `BaseAIFunction.parse_response()` | 75 | Abstract method for parsing AI response |
| Request/Response | Request body format | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 171-183 | Format: {'model': str, 'messages': [...], 'temperature': float, 'max_tokens': int, 'response_format': dict} |
| Request/Response | Response format | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 215-329 | Returns: {'content': str, 'input_tokens': int, 'output_tokens': int, 'total_tokens': int, 'model': str, 'cost': float, 'error': str, 'api_id': str} |
| Request/Response | JSON parsing fallback | `backend/igny8_core/ai/functions/auto_cluster.py` | `parse_response()` | 173-176 | Falls back to `extract_json()` if direct JSON parse fails |
| **📍 Paths / Constants** |
| Path/Constant | `OPENAI_API_KEY` | `backend/igny8_core/utils/ai_processor.py` | Django setting | 93 | Fallback API key from Django settings |
| Path/Constant | `RUNWARE_API_KEY` | `backend/igny8_core/utils/ai_processor.py` | Django setting | 95 | Fallback API key from Django settings |
| Path/Constant | OpenAI API URL | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 163 | Hardcoded: 'https://api.openai.com/v1/chat/completions' |
| Path/Constant | OpenAI Images URL | `backend/igny8_core/utils/ai_processor.py` | `generate_image()` | 735 | Hardcoded: 'https://api.openai.com/v1/images/generations' |
| Path/Constant | Runware API URL | `backend/igny8_core/utils/ai_processor.py` | `generate_image()` | 844 | Hardcoded: 'https://api.runware.ai/v1' |
| Path/Constant | `IntegrationSettings.config['apiKey']` | `backend/igny8_core/modules/system/models.py` | `IntegrationSettings.config` | 56 | JSONField storing API key per account |
| Path/Constant | Prompt type choices | `backend/igny8_core/modules/system/models.py` | `AIPrompt.PROMPT_TYPE_CHOICES` | 16 | ['clustering', 'ideas', 'content_generation', 'image_prompt_extraction', 'image_prompt_template', 'negative_prompt'] |
| Path/Constant | Integration type choices | `backend/igny8_core/modules/system/models.py` | `IntegrationSettings.INTEGRATION_TYPE_CHOICES` | 48 | ['openai', 'runware', 'gsc', 'image_generation'] |
| Path/Constant | `get_function_instance` | `backend/igny8_core/ai/registry.py` | `get_function_instance()` | - | Gets AI function instance from registry |
| Path/Constant | `auto_cluster` | `backend/igny8_core/ai/registry.py` | `register_lazy_function()` | 69 | Registered function name for clustering |
| **💰 Cost Tracking** |
| Cost | `CostTracker` | `backend/igny8_core/ai/tracker.py` | `CostTracker` class | 193 | Tracks API costs and token usage |
| Cost | `record` | `backend/igny8_core/ai/tracker.py` | `CostTracker.record()` | 201 | Records cost, tokens, model for an operation |
| Cost | Cost calculation | `backend/igny8_core/utils/ai_processor.py` | `_call_openai()` | 277-295 | Calculates cost from MODEL_RATES based on input/output tokens |
| Cost | Image cost calculation | `backend/igny8_core/utils/ai_processor.py` | `generate_image()` | 790 | Calculates cost from IMAGE_MODEL_RATES * n images |
| Cost | Credit logging | `backend/igny8_core/ai/engine.py` | `execute()` | 142-171 | Logs credit usage to CreditUsageLog after successful save |
| Cost | `_calculate_credits_for_clustering` | `backend/igny8_core/ai/engine.py` | `_calculate_credits_for_clustering()` | 257 | Calculates credits used (from plan config or fallback formula) |
| **📊 Progress Tracking** |
| Progress | `ProgressTracker` | `backend/igny8_core/ai/tracker.py` | `ProgressTracker` class | 77 | Tracks progress updates for AI tasks |
| Progress | `update` | `backend/igny8_core/ai/tracker.py` | `ProgressTracker.update()` | 89 | Updates Celery task state with progress |
| Progress | Phase percentages | `backend/igny8_core/ai/engine.py` | `execute()` | 30-36 | INIT (0-10%), PREP (10-25%), AI_CALL (25-70%), PARSE (70-85%), SAVE (85-98%), DONE (98-100%) |
| Progress | `update_ai_progress` | `backend/igny8_core/ai/tracker.py` | `ProgressTracker.update_ai_progress()` | 184 | Callback for AI processor progress updates |
| **🗄️ Database Logging** |
| Database | `AITaskLog` | `backend/igny8_core/ai/models.py` | `AITaskLog` model | 8 | Unified logging table for all AI tasks |
| Database | `_log_to_database` | `backend/igny8_core/ai/engine.py` | `AIEngine._log_to_database()` | 220 | Logs task execution to AITaskLog table |
| Database | Task logging | `backend/igny8_core/ai/engine.py` | `execute()` | 178 | Called after successful execution to log to database |
| **🔄 Celery Integration** |
| Celery | `run_ai_task` | `backend/igny8_core/ai/tasks.py` | `run_ai_task()` | 13 | Unified Celery task entrypoint |
| Celery | Task state updates | `backend/igny8_core/ai/tracker.py` | `ProgressTracker.update()` | 124-131 | Updates Celery task state via `task.update_state()` |
| Celery | Meta injection | `backend/igny8_core/modules/system/integration_views.py` | `task_progress()` | 936-991 | Extracts request_steps/response_steps from task meta and returns to frontend |
| Celery | Error state | `backend/igny8_core/ai/tasks.py` | `run_ai_task()` | 68-84 | Updates task state to FAILURE on error |
| **🔧 Utility Functions** |
| Utility | `get_default_prompt` | `backend/igny8_core/modules/system/utils.py` | `get_default_prompt()` | 7 | Returns hardcoded default prompt by type |
| Utility | `get_prompt_value` | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` | 108 | Gets prompt from DB or returns default |
| Utility | `check_moderation` | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor.check_moderation()` | 594 | Checks content against OpenAI moderation API |
| Utility | `prepare` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.prepare()` | 85 | Loads keywords with relationships |
| Utility | `save_output` | `backend/igny8_core/ai/functions/auto_cluster.py` | `AutoClusterFunction.save_output()` | 205 | Saves clusters to database with transaction |

View File

@@ -0,0 +1,300 @@
# IGNY8 AI Elements Reference Table
Generated by extract_ai_elements.py analysis
---
## 🧠 AI Core Functions
| Function Name | Category | Type | File | Line | Uses AIProcessor | Celery | Progress | Steps | Prompt Source | Model Source |
|---------------|----------|------|------|------|------------------|--------|----------|-------|---------------|--------------|
| `_auto_cluster_keywords_core` | cluster | core_function | `backend/igny8_core/modules/planner/tasks.py` | 26 | ✅ | ❌ | ✅ | ✅ | Database (get_prompt_value) | AIProcessor.default_model |
| `_generate_single_idea_core` | ideas | core_function | `backend/igny8_core/modules/planner/tasks.py` | 1047 | ✅ | ❌ | ✅ | ✅ | Database (get_prompt_value) | AIProcessor.default_model |
| `auto_generate_content_task` | content | celery_task | `backend/igny8_core/modules/writer/tasks.py` | 27 | ✅ | ✅ | ✅ | ❌ | Database (get_prompt_value) | AIProcessor.default_model |
| `auto_generate_images_task` | image | celery_task | `backend/igny8_core/modules/writer/tasks.py` | 741 | ✅ | ✅ | ✅ | ❌ | Database (get_prompt_value) | AIProcessor.default_model |
| `AutoClusterFunction` | cluster | class | `backend/igny8_core/ai/functions/auto_cluster.py` | 14 | ✅ | ❌ | ✅ | ✅ | Database (get_prompt_value) | Function.get_model() |
| `cluster_keywords` | cluster | method | `backend/igny8_core/utils/ai_processor.py` | 1080 | ✅ | ❌ | ✅ | ✅ | Inline/Hardcoded | AIProcessor.default_model |
| `generate_ideas` | ideas | method | `backend/igny8_core/utils/ai_processor.py` | 1280 | ✅ | ❌ | ✅ | ✅ | Inline/Hardcoded | AIProcessor.default_model |
| `generate_content` | content | method | `backend/igny8_core/utils/ai_processor.py` | 446 | ✅ | ❌ | ❌ | ❌ | Inline/Hardcoded | AIProcessor.default_model |
| `generate_image` | image | method | `backend/igny8_core/utils/ai_processor.py` | 656 | ✅ | ❌ | ❌ | ❌ | Inline/Hardcoded | Parameter or default |
| `run_ai_task` | unified | celery_task | `backend/igny8_core/ai/tasks.py` | 13 | ❌ | ✅ | ✅ | ✅ | Via function | Via function |
| `execute` | unified | method | `backend/igny8_core/ai/engine.py` | 26 | ✅ | ❌ | ✅ | ✅ | Via function | Via function |
---
## 🧱 Prompt Sources
| Prompt Type | Source | File | Retrieval Method |
|-------------|--------|------|------------------|
| `clustering` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
| `ideas` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
| `content_generation` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
| `image_prompt_extraction` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
| `image_prompt_template` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
| `negative_prompt` | Hardcoded in get_default_prompt() | `backend/igny8_core/modules/system/utils.py` | `get_prompt_value()` -> AIPrompt model or default |
**Prompt Storage:**
- **Database Model**: `AIPrompt` in `backend/igny8_core/modules/system/models.py`
- **Table**: `igny8_ai_prompts`
- **Fields**: `prompt_type`, `prompt_value`, `default_prompt`, `account` (FK)
- **Unique Constraint**: `(account, prompt_type)`
**Prompt Retrieval Flow:**
1. `get_prompt_value(account, prompt_type)` in `modules/system/utils.py:108`
2. Tries: `AIPrompt.objects.get(account=account, prompt_type=prompt_type, is_active=True)`
3. Falls back to: `get_default_prompt(prompt_type)` if not found
---
## 🧾 Model Configuration
| Model Name | Source | File | Selection Method |
|------------|--------|------|------------------|
| `gpt-4.1` | MODEL_RATES constant | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_model()` -> IntegrationSettings or Django settings |
| `gpt-4o-mini` | MODEL_RATES constant | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_model()` -> IntegrationSettings or Django settings |
| `gpt-4o` | MODEL_RATES constant | `backend/igny8_core/utils/ai_processor.py` | `AIProcessor._get_model()` -> IntegrationSettings or Django settings |
| `dall-e-3` | IMAGE_MODEL_RATES constant | `backend/igny8_core/utils/ai_processor.py` | Parameter or default in `generate_image()` |
| `dall-e-2` | IMAGE_MODEL_RATES constant | `backend/igny8_core/utils/ai_processor.py` | Parameter or default in `generate_image()` |
**Model Selection Flow:**
1. `AIProcessor.__init__(account)` in `utils/ai_processor.py:54`
2. Calls `_get_model('openai', account)` in `utils/ai_processor.py:98`
3. Tries: `IntegrationSettings.objects.filter(integration_type='openai', account=account, is_active=True).first().config.get('model')`
4. Validates model is in `MODEL_RATES` dict
5. Falls back to: `settings.DEFAULT_AI_MODEL` (default: 'gpt-4.1')
**Model Storage:**
- **Database Model**: `IntegrationSettings` in `backend/igny8_core/modules/system/models.py`
- **Table**: `igny8_integration_settings`
- **Fields**: `integration_type`, `config` (JSONField), `account` (FK)
- **Config Structure**: `{"apiKey": "...", "model": "gpt-4.1", "enabled": true}`
---
## ⚠️ Validation & Limits
| Function | Validation Checks | Limit Checks |
|----------|-------------------|--------------|
| `_auto_cluster_keywords_core` | Has validate() call, Keywords exist check | Credit check, Plan limits (daily_cluster_limit, max_clusters) |
| `AutoClusterFunction.validate()` | Base validation (ids array, max_items), Keywords exist | Plan limits (daily_cluster_limit, max_clusters) |
| `auto_generate_content_task` | Task existence, Account validation | Credit check (via CreditService) |
| `auto_generate_images_task` | Task existence, Account validation | Credit check (via CreditService) |
| `generate_image` | Model validation (VALID_OPENAI_IMAGE_MODELS), Size validation (VALID_SIZES_BY_MODEL) | None |
| `AIProcessor._get_model()` | Model in MODEL_RATES validation | None |
**Validation Details:**
1. **Plan Limits** (in `AutoClusterFunction.validate()`):
- `plan.daily_cluster_limit` - Max clusters per day
- `plan.max_clusters` - Total max clusters
- Checked in `backend/igny8_core/ai/functions/auto_cluster.py:59-79`
2. **Credit Checks**:
- `CreditService.check_credits(account, required_credits)` in `modules/billing/services.py:16`
- Used before AI operations
3. **Model Validation**:
- OpenAI images: Only `dall-e-3` and `dall-e-2` valid (line 704-708 in `ai_processor.py`)
- Size validation per model (line 719-724 in `ai_processor.py`)
4. **Input Validation**:
- Base validation in `BaseAIFunction.validate()` checks for 'ids' array and max_items limit
- `AutoClusterFunction.get_max_items()` returns 20 (max keywords per cluster)
---
## 🔁 Retry & Error Handling
| Component | Retry Logic | Error Handling | Fallback |
|-----------|-------------|----------------|----------|
| `run_ai_task` | `max_retries=3` (Celery decorator) | Exception caught, task state updated to FAILURE | None |
| `auto_generate_content_task` | `max_retries=3` (Celery decorator) | Try/except blocks, error logging | None |
| `_call_openai` | None (single attempt) | HTTP error handling, JSON parse errors, timeout (60s) | Returns error dict |
| `_get_api_key` | None | Exception caught, logs warning | Falls back to Django settings (`OPENAI_API_KEY`, `RUNWARE_API_KEY`) |
| `_get_model` | None | Exception caught, logs warning | Falls back to Django settings (`DEFAULT_AI_MODEL`) |
---
## 🪵 AI Debug Steps
| Function | Request Steps | Response Steps | Step Tracking Method |
|----------|---------------|----------------|---------------------|
| `_auto_cluster_keywords_core` | ✅ (manual list) | ✅ (manual list) | Manual `request_steps.append()` and `response_steps.append()` |
| `AutoClusterFunction` | ✅ (via StepTracker) | ✅ (via StepTracker) | `StepTracker.add_request_step()` and `add_response_step()` |
| `run_ai_task` | ✅ (via engine) | ✅ (via engine) | Extracted from `engine.execute()` result |
| `AIEngine.execute` | ✅ (via StepTracker) | ✅ (via StepTracker) | `StepTracker` instance tracks all steps |
| `auto_generate_content_task` | ❌ | ❌ | No step tracking (legacy) |
| `auto_generate_images_task` | ❌ | ❌ | No step tracking (legacy) |
**Step Tracking Implementation:**
1. **New Framework** (AIEngine):
- Uses `StepTracker` class in `backend/igny8_core/ai/tracker.py`
- Steps added at each phase: INIT, PREP, AI_CALL, PARSE, SAVE, DONE
- Steps stored in `request_steps` and `response_steps` lists
- Returned in result dict and logged to `AITaskLog` model
2. **Legacy Functions**:
- Manual step tracking with lists
- Steps added to `meta` dict for Celery task progress
- Extracted in `integration_views.py:task_progress()` for frontend
3. **Step Structure**:
```python
{
'stepNumber': int,
'stepName': str, # INIT, PREP, AI_CALL, PARSE, SAVE, DONE
'functionName': str,
'status': 'success' | 'error' | 'pending',
'message': str,
'error': str (optional),
'duration': int (milliseconds, optional)
}
```
---
## 📦 Request/Response Structuring
| Function | Request Format | Response Format | JSON Mode | Parsing Method |
|----------|----------------|-----------------|-----------|----------------|
| `_call_openai` | OpenAI API format: `{'model': str, 'messages': [...], 'temperature': float, 'max_tokens': int, 'response_format': dict}` | `{'content': str, 'input_tokens': int, 'output_tokens': int, 'total_tokens': int, 'model': str, 'cost': float, 'error': str, 'api_id': str}` | ✅ (if `response_format={'type': 'json_object'}`) | `_extract_json_from_response()` |
| `cluster_keywords` | Prompt string with keywords | JSON with 'clusters' array | ✅ (auto-enabled for json_models) | `_extract_json_from_response()` then extract 'clusters' |
| `generate_ideas` | Prompt string with clusters | JSON with 'ideas' array | ✅ (auto-enabled for json_models) | `_extract_json_from_response()` then extract 'ideas' |
| `generate_image` (OpenAI) | `{'prompt': str, 'model': str, 'n': int, 'size': str}` | `{'url': str, 'revised_prompt': str, 'cost': float}` | N/A | Direct JSON response |
| `generate_image` (Runware) | Array format with `imageInference` tasks | `{'url': str, 'cost': float}` | N/A | Extract from nested response structure |
**JSON Mode Auto-Enable:**
- Models: `['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo-preview']`
- Auto-enabled in `AIProcessor.call()` if `response_format` not specified
- Location: `backend/igny8_core/ai/processor.py:40-42`
**JSON Extraction:**
- Primary: Direct `json.loads()` on response
- Fallback: `_extract_json_from_response()` handles:
- Markdown code blocks (```json ... ```)
- Multiline JSON
- Partial JSON extraction
- Location: `backend/igny8_core/utils/ai_processor.py:334-440`
---
## 📍 Paths & Constants
| Constant | Value | File | Usage |
|----------|-------|------|-------|
| `OPENAI_API_KEY` | Django setting | `backend/igny8_core/utils/ai_processor.py:93` | Fallback API key |
| `RUNWARE_API_KEY` | Django setting | `backend/igny8_core/utils/ai_processor.py:95` | Fallback API key |
| `DEFAULT_AI_MODEL` | Django setting (default: 'gpt-4.1') | `backend/igny8_core/utils/ai_processor.py:121` | Fallback model |
| OpenAI API URL | `'https://api.openai.com/v1/chat/completions'` | `backend/igny8_core/utils/ai_processor.py:163` | Text generation endpoint |
| OpenAI Images URL | `'https://api.openai.com/v1/images/generations'` | `backend/igny8_core/utils/ai_processor.py:735` | Image generation endpoint |
| Runware API URL | `'https://api.runware.ai/v1'` | `backend/igny8_core/utils/ai_processor.py:844` | Runware image generation |
| `MODEL_RATES` | Dict with pricing per 1M tokens | `backend/igny8_core/utils/ai_processor.py:19` | Cost calculation |
| `IMAGE_MODEL_RATES` | Dict with pricing per image | `backend/igny8_core/utils/ai_processor.py:26` | Image cost calculation |
| `VALID_OPENAI_IMAGE_MODELS` | `{'dall-e-3', 'dall-e-2'}` | `backend/igny8_core/utils/ai_processor.py:34` | Model validation |
| `VALID_SIZES_BY_MODEL` | Dict mapping models to valid sizes | `backend/igny8_core/utils/ai_processor.py:41` | Size validation |
---
## 💰 Cost Tracking
| Component | Cost Calculation | Token Tracking | Storage |
|-----------|------------------|----------------|---------|
| `_call_openai` | Calculated from `MODEL_RATES` based on input/output tokens | ✅ (input_tokens, output_tokens, total_tokens) | Returned in result dict |
| `generate_image` (OpenAI) | `IMAGE_MODEL_RATES[model] * n` | N/A | Returned in result dict |
| `generate_image` (Runware) | `0.036 * n` (hardcoded) | N/A | Returned in result dict |
| `CostTracker` | Aggregates costs from multiple operations | ✅ (total_tokens) | In-memory during execution |
| `AITaskLog` | Stored in `cost` field (DecimalField) | ✅ (stored in `tokens` field) | Database table `igny8_ai_task_logs` |
| `CreditUsageLog` | Stored in `cost_usd` field | ✅ (tokens_input, tokens_output) | Database table (billing module) |
**Cost Calculation Formula:**
```python
# Text generation
input_cost = (input_tokens / 1_000_000) * MODEL_RATES[model]['input']
output_cost = (output_tokens / 1_000_000) * MODEL_RATES[model]['output']
total_cost = input_cost + output_cost
# Image generation
cost = IMAGE_MODEL_RATES[model] * n_images
```
---
## 📊 Progress Tracking
| Function | Progress Method | Phase Tracking | Percentage Mapping |
|----------|-----------------|----------------|-------------------|
| `_auto_cluster_keywords_core` | `progress_callback()` function | Manual phase strings | Manual percentage |
| `auto_generate_content_task` | `self.update_state()` (Celery) | Manual phase strings | Manual percentage |
| `AIEngine.execute` | `ProgressTracker.update()` | Automatic (INIT, PREP, AI_CALL, PARSE, SAVE, DONE) | Automatic: INIT (0-10%), PREP (10-25%), AI_CALL (25-70%), PARSE (70-85%), SAVE (85-98%), DONE (98-100%) |
| `run_ai_task` | Via `AIEngine` | Via `AIEngine` | Via `AIEngine` |
**Progress Tracker:**
- Class: `ProgressTracker` in `backend/igny8_core/ai/tracker.py:77`
- Updates Celery task state via `task.update_state()`
- Tracks: phase, percentage, message, current, total, meta
---
## 🗄️ Database Logging
| Component | Log Table | Fields Logged | When Logged |
|-----------|-----------|---------------|-------------|
| `AIEngine.execute` | `AITaskLog` | task_id, function_name, phase, message, status, duration, cost, tokens, request_steps, response_steps, error, payload, result | After execution (success or error) |
| Credit usage | `CreditUsageLog` | account, operation_type, credits_used, cost_usd, model_used, tokens_input, tokens_output | After successful save operation |
**AITaskLog Model:**
- Table: `igny8_ai_task_logs`
- Location: `backend/igny8_core/ai/models.py:8`
- Fields: All execution details including steps, costs, tokens, errors
---
## 🔄 Celery Integration
| Task | Entrypoint | Task ID | State Updates | Error Handling |
|------|------------|---------|---------------|----------------|
| `run_ai_task` | `backend/igny8_core/ai/tasks.py:13` | `self.request.id` | Via `ProgressTracker` | Updates state to FAILURE, raises exception |
| `auto_generate_content_task` | `backend/igny8_core/modules/writer/tasks.py:27` | `self.request.id` | Manual `self.update_state()` | Try/except, logs error |
| `auto_generate_images_task` | `backend/igny8_core/modules/writer/tasks.py:741` | `self.request.id` | Manual `self.update_state()` | Try/except, logs error |
**Task Progress Endpoint:**
- Route: `/api/v1/system/settings/task_progress/{task_id}/`
- Handler: `IntegrationSettingsViewSet.task_progress()` in `modules/system/integration_views.py:936`
- Extracts: `request_steps` and `response_steps` from task meta
- Returns: Progress data to frontend for debug panel
---
## Summary
**Key Findings:**
1. **Two AI Systems Coexist:**
- **Legacy**: Direct functions in `modules/planner/tasks.py` and `modules/writer/tasks.py`
- **New Framework**: `AIEngine` + `BaseAIFunction` classes in `ai/` directory
2. **Unified Entrypoint:**
- `run_ai_task()` in `ai/tasks.py` is the unified Celery entrypoint
- Uses `AIEngine` to execute any registered AI function
3. **Prompt Management:**
- All prompts stored in `AIPrompt` model (database)
- Fallback to hardcoded defaults in `get_default_prompt()`
- Retrieved via `get_prompt_value(account, prompt_type)`
4. **Model Selection:**
- Per-account via `IntegrationSettings.config['model']`
- Falls back to Django `DEFAULT_AI_MODEL` setting
- Validated against `MODEL_RATES` dict
5. **Step Tracking:**
- New framework uses `StepTracker` class
- Legacy functions use manual lists
- Both stored in Celery task meta and `AITaskLog` model
6. **Cost Tracking:**
- Calculated from `MODEL_RATES` and `IMAGE_MODEL_RATES`
- Logged to `AITaskLog` and `CreditUsageLog`
- Tracked via `CostTracker` during execution

View File

@@ -0,0 +1,121 @@
# Color Tokens Comparison
Side-by-side comparison of provided tokens vs. global color palette
| Color Token | Provided Value | Our Global Palette | Match Status | Notes |
|-------------|----------------|-------------------|--------------|-------|
| **RED** |
| `--color-red-50` | `oklch(.971 .013 17.38)` | `oklch(0.971 0.013 17.38)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:10` |
| `--color-red-100` | `oklch(.936 .032 17.717)` | `oklch(0.936 0.032 17.717)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:11` |
| `--color-red-500` | `oklch(.637 .237 25.331)` | `oklch(0.637 0.237 25.331)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:15` |
| `--color-red-600` | `oklch(.577 .245 27.325)` | `oklch(0.577 0.245 27.325)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:16` |
| `--color-red-700` | `oklch(.505 .213 27.518)` | `oklch(0.505 0.213 27.518)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:17` |
| **ORANGE** |
| `--color-orange-50` | `#fff6ed` | `oklch(0.98 0.016 73.684)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:22`) |
| `--color-orange-100` | `#ffead5` | `oklch(0.954 0.038 75.164)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:23`) |
| `--color-orange-400` | `#fd853a` | `oklch(0.75 0.183 55.934)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:26`) |
| `--color-orange-500` | `#fb6514` | `oklch(0.705 0.213 47.604)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:27`) |
| `--color-orange-600` | `#ec4a0a` | `oklch(0.646 0.222 41.116)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:28`) |
| `--color-orange-700` | `#c4320a` | `oklch(0.553 0.195 38.402)` ❌ | **DIFFERENT** | Provided: hex, Ours: oklch (in `tailwindcss/theme.css:29`) |
| **YELLOW** |
| `--color-yellow-100` | `oklch(.973 .071 103.193)` | `oklch(0.973 0.071 103.193)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:47` |
| `--color-yellow-600` | `oklch(.681 .162 75.834)` | `oklch(0.681 0.162 75.834)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:52` |
| **GREEN** |
| `--color-green-50` | `oklch(.982 .018 155.826)` | `oklch(0.982 0.018 155.826)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:70` |
| `--color-green-100` | `oklch(.962 .044 156.743)` | `oklch(0.962 0.044 156.743)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:71` |
| `--color-green-500` | `oklch(.723 .219 149.579)` | `oklch(0.723 0.219 149.579)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:75` |
| `--color-green-600` | `oklch(.627 .194 149.214)` | `oklch(0.627 0.194 149.214)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:76` |
| `--color-green-700` | `oklch(.527 .154 150.069)` | `oklch(0.527 0.154 150.069)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:77` |
| **CYAN** |
| `--color-cyan-100` | `oklch(.956 .045 203.388)` | `oklch(0.956 0.045 203.388)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:107` |
| `--color-cyan-600` | `oklch(.609 .126 221.723)` | `oklch(0.609 0.126 221.723)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:112` |
| **BLUE** |
| `--color-blue-50` | `oklch(.97 .014 254.604)` | `oklch(0.97 0.014 254.604)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:130` |
| `--color-blue-100` | `oklch(.932 .032 255.585)` | `oklch(0.932 0.032 255.585)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:131` |
| `--color-blue-500` | `oklch(.623 .214 259.815)` | `oklch(0.623 0.214 259.815)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:135` |
| `--color-blue-700` | `oklch(.488 .243 264.376)` | `oklch(0.488 0.243 264.376)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:137` |
| **PURPLE** |
| `--color-purple-50` | `oklch(.977 .014 308.299)` | `oklch(0.977 0.014 308.299)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:166` |
| `--color-purple-100` | `oklch(.946 .033 307.174)` | `oklch(0.946 0.033 307.174)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:167` |
| `--color-purple-400` | `oklch(.714 .203 305.504)` | `oklch(0.714 0.203 305.504)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:170` |
| `--color-purple-500` | `oklch(.627 .265 303.9)` | `oklch(0.627 0.265 303.9)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:171` |
| `--color-purple-600` | `oklch(.558 .288 302.321)` | `oklch(0.558 0.288 302.321)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:172` |
| `--color-purple-700` | `oklch(.496 .265 301.924)` | `oklch(0.496 0.265 301.924)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:173` |
| **PINK** |
| `--color-pink-100` | `oklch(.948 .028 342.258)` | `oklch(0.948 0.028 342.258)` ✅ | **EXACT MATCH** | In `tailwindcss/theme.css:191` |
| `--color-pink-600` | `oklch(.592 .249 .584)` | `oklch(0.592 0.249 0.584)` ⚠️ | **FORMAT DIFFERENCE** | Provided: `.584` (missing hue), Ours: full oklch (in `tailwindcss/theme.css:196`) |
| **GRAY** |
| `--color-gray-50` | `#f9fafb` | `#f9fafb` ✅ | **EXACT MATCH** | In `index.css:87` |
| `--color-gray-100` | `#f2f4f7` | `#f2f4f7` ✅ | **EXACT MATCH** | In `index.css:88` |
| `--color-gray-200` | `#e4e7ec` | `#e4e7ec` ✅ | **EXACT MATCH** | In `index.css:89` |
| `--color-gray-300` | `#d0d5dd` | `#d0d5dd` ✅ | **EXACT MATCH** | In `index.css:90` |
| `--color-gray-400` | `#98a2b3` | `#98a2b3` ✅ | **EXACT MATCH** | In `index.css:91` |
| `--color-gray-500` | `#667085` | `#667085` ✅ | **EXACT MATCH** | In `index.css:92` |
| `--color-gray-600` | `#475467` | `#475467` ✅ | **EXACT MATCH** | In `index.css:93` |
| `--color-gray-700` | `#344054` | `#344054` ✅ | **EXACT MATCH** | In `index.css:94` |
| `--color-gray-800` | `#1d2939` | `#1d2939` ✅ | **EXACT MATCH** | In `index.css:95` |
| `--color-gray-900` | `#101828` | `#101828` ✅ | **EXACT MATCH** | In `index.css:96` |
| `--color-gray-950` | `#0c111d` | `#0c111d` ✅ | **EXACT MATCH** | In `index.css:97` |
| **BLACK & WHITE** |
| `--color-black` | `#101828` | `#101828` ✅ | **EXACT MATCH** | In `index.css:58` |
| `--color-white` | `#fff` | `#ffffff` ✅ | **EQUIVALENT** | In `index.css:57` (same color, different format) |
---
## Summary
### ✅ **Matches (28 tokens)**
- **Red**: All 5 tokens match exactly (oklch format)
- **Yellow**: Both tokens match exactly (oklch format)
- **Green**: All 5 tokens match exactly (oklch format)
- **Cyan**: Both tokens match exactly (oklch format)
- **Blue**: All 4 tokens match exactly (oklch format)
- **Purple**: All 6 tokens match exactly (oklch format)
- **Gray**: All 11 tokens match exactly (hex format)
- **Black/White**: Both match (hex format)
### ❌ **Differences (6 tokens)**
- **Orange**: All 6 tokens differ
- Provided: Hex format (`#fff6ed`, `#ffead5`, etc.)
- Ours: oklch format in Tailwind theme
- **Note**: These are likely the same colors, just different formats. The hex values you provided match our custom orange palette in `index.css:100-111` (which uses hex), but Tailwind's theme uses oklch.
### ⚠️ **Format Issue (1 token)**
- **Pink-600**: `oklch(.592 .249 .584)` - Missing hue value (should be 3 values: lightness, chroma, hue)
---
## Location of Colors in Codebase
### Tailwind Default Colors (oklch format)
- **File**: `frontend/node_modules/tailwindcss/theme.css`
- Contains: red, orange, yellow, green, cyan, blue, purple, pink (all in oklch)
### Custom IGNY8 Colors (hex format)
- **File**: `frontend/src/index.css` (lines 55-154)
- Contains:
- Gray scale (hex)
- Orange scale (hex) - **Matches your provided orange values!**
- Brand colors (hex)
- Success/Error/Warning colors (hex)
### IGNY8 Brand Colors
- **File**: `frontend/src/styles/igny8-colors.css`
- Contains: Custom brand colors with `--igny8-` prefix
---
## Recommendation
**Your provided orange tokens match our custom orange palette in `index.css`!**
The orange colors you provided are already in our codebase:
- `--color-orange-50: #fff6ed` ✅ (line 101)
- `--color-orange-100: #ffead5` ✅ (line 102)
- `--color-orange-400: #fd853a` ✅ (line 105)
- `--color-orange-500: #fb6514` ✅ (line 106)
- `--color-orange-600: #ec4a0a` ✅ (line 107)
- `--color-orange-700: #c4320a` ✅ (line 108)
All other colors (red, yellow, green, cyan, blue, purple, gray, black, white) also match either in Tailwind's theme or our custom palette.

1153
docs/MASTER-ARCHITECTURE.md Normal file

File diff suppressed because it is too large Load Diff